From 4218656483190b2b433efe86be327c33a2820146 Mon Sep 17 00:00:00 2001 From: wlinator Date: Tue, 10 Sep 2024 09:00:09 -0400 Subject: [PATCH 01/38] Add `reminder_sent` attribute to the database --- prisma/schema.prisma | 1 + 1 file changed, 1 insertion(+) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 777a9cc..f5cd1b4 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -110,6 +110,7 @@ model Reminder { reminder_expires_at DateTime reminder_channel_id BigInt reminder_user_id BigInt + reminder_sent Boolean @default(false) guild_id BigInt guild Guild @relation(fields: [guild_id], references: [guild_id]) From 7625999cb17ab2a190ce1ea5bd2ab799548f5e08 Mon Sep 17 00:00:00 2001 From: wlinator Date: Tue, 10 Sep 2024 09:00:28 -0400 Subject: [PATCH 02/38] core(remindme): Update the database controller --- tux/database/controllers/reminder.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tux/database/controllers/reminder.py b/tux/database/controllers/reminder.py index d0c73fa..e6fd1a3 100644 --- a/tux/database/controllers/reminder.py +++ b/tux/database/controllers/reminder.py @@ -38,6 +38,7 @@ class ReminderController: "reminder_expires_at": reminder_expires_at, "reminder_channel_id": reminder_channel_id, "guild_id": guild_id, + "reminder_sent": False, }, ) @@ -53,3 +54,19 @@ class ReminderController: where={"reminder_id": reminder_id}, data={"reminder_content": reminder_content}, ) + + async def update_reminder_status(self, reminder_id: int, sent: bool = True) -> None: + """ + Update the status of a reminder. This sets the value "reminder_sent" to True by default. + + Parameters + ---------- + reminder_id : int + The ID of the reminder to update. + sent : bool + The new status of the reminder. + """ + await self.table.update( + where={"reminder_id": reminder_id}, + data={"reminder_sent": sent}, + ) From 49c71d7ba7376c9e38c2c5a77b18909103b5dbfb Mon Sep 17 00:00:00 2001 From: wlinator Date: Tue, 10 Sep 2024 09:03:30 -0400 Subject: [PATCH 03/38] chore(database): Add method to retrieve unsent reminders --- tux/database/controllers/reminder.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tux/database/controllers/reminder.py b/tux/database/controllers/reminder.py index e6fd1a3..95f30f0 100644 --- a/tux/database/controllers/reminder.py +++ b/tux/database/controllers/reminder.py @@ -21,6 +21,9 @@ class ReminderController: async def get_reminder_by_id(self, reminder_id: int) -> Reminder | None: return await self.table.find_first(where={"reminder_id": reminder_id}) + async def get_unsent_reminders(self) -> list[Reminder]: + return await self.table.find_many(where={"reminder_sent": False}) + async def insert_reminder( self, reminder_user_id: int, From d8300957a4f6f2f4dbfa244d808ba5d5064c0001 Mon Sep 17 00:00:00 2001 From: wlinator Date: Tue, 10 Sep 2024 09:13:27 -0400 Subject: [PATCH 04/38] feat(database): Add more information in the command embed --- tux/cogs/utility/remindme.py | 133 ++++++----------------------------- 1 file changed, 23 insertions(+), 110 deletions(-) diff --git a/tux/cogs/utility/remindme.py b/tux/cogs/utility/remindme.py index 6c84c74..53b64e1 100644 --- a/tux/cogs/utility/remindme.py +++ b/tux/cogs/utility/remindme.py @@ -1,10 +1,9 @@ -import asyncio import contextlib import datetime import discord from discord import app_commands -from discord.ext import commands +from discord.ext import commands, tasks from loguru import logger from prisma.models import Reminder @@ -14,40 +13,24 @@ from tux.ui.embeds import EmbedCreator from tux.utils.functions import convert_to_seconds -def get_closest_reminder(reminders: list[Reminder]) -> Reminder | None: - """ - Check if there are any reminders and return the closest one. - - - Parameters - ---------- - reminders : list[Reminder] - A list of reminders to check. - - Returns - ------- - Reminder | None - The closest reminder or None if there are no reminders. - """ - return min(reminders, key=lambda x: x.reminder_expires_at) if reminders else None - - class RemindMe(commands.Cog): def __init__(self, bot: Tux) -> None: self.bot = bot self.db = DatabaseController().reminder - self.bot.loop.create_task(self.update()) + self.check_reminders.start() - async def send_reminders(self, reminder: Reminder) -> None: - """ - Send the reminder to the user. + @tasks.loop(minutes=5) + async def check_reminders(self): + now = datetime.datetime.now(datetime.UTC) + reminders = await self.db.get_unsent_reminders() - Parameters - ---------- - reminder : Reminder - The reminder object. - """ + for reminder in reminders: + if not reminder.reminder_sent and reminder.reminder_expires_at <= now: + await self.send_reminder(reminder) + await self.db.update_reminder_status(reminder.reminder_id, sent=True) + logger.debug(f'Status of reminder {reminder.reminder_id} updated to "sent".') + async def send_reminder(self, reminder: Reminder) -> None: user = self.bot.get_user(reminder.reminder_user_id) if user is not None: @@ -64,20 +47,15 @@ class RemindMe(commands.Cog): await user.send(embed=embed) except discord.Forbidden: - # Send a message in the channel if the user has DMs closed - channel: discord.abc.GuildChannel | discord.Thread | discord.abc.PrivateChannel | None = ( - self.bot.get_channel(reminder.reminder_channel_id) - ) + channel = self.bot.get_channel(reminder.reminder_channel_id) - if channel is not None and isinstance( - channel, - discord.TextChannel | discord.Thread | discord.VoiceChannel, - ): + if isinstance(channel, discord.TextChannel | discord.Thread | discord.VoiceChannel): with contextlib.suppress(discord.Forbidden): await channel.send( content=f"{user.mention} Failed to DM you, sending in channel", embed=embed, ) + return else: logger.error( @@ -87,80 +65,17 @@ class RemindMe(commands.Cog): else: logger.error(f"Failed to send reminder to {reminder.reminder_user_id}, user not found.") - # Delete the reminder after sending - await self.db.delete_reminder_by_id(reminder.reminder_id) - - # wait for a second so that the reminder is deleted before checking for more reminders - # who knows if this works, it seems to - await asyncio.sleep(1) - - # Run update again to check if there are any more reminders - await self.update() - - async def end_timer(self, reminder: Reminder) -> None: - """ - End the timer for the reminder. - - Parameters - ---------- - reminder : Reminder - The reminder object. - """ - - # Wait until the reminder expires - await discord.utils.sleep_until(reminder.reminder_expires_at) - await self.send_reminders(reminder) - - async def update(self) -> None: - """ - Update the reminders - - Check if there are any reminders and send the closest one. - """ - - try: - # Get all reminders - reminders = await self.db.get_all_reminders() - # Get the closest reminder - closest_reminder = get_closest_reminder(reminders) - - except Exception as e: - logger.error(f"Error getting reminders: {e}") - return - - # If there are no reminders, return - if closest_reminder is None: - return - - # Check if it's expired - if closest_reminder.reminder_expires_at < datetime.datetime.now(datetime.UTC): - await self.send_reminders(closest_reminder) - return - - # Create a task to wait until the reminder expires - self.bot.loop.create_task(self.end_timer(closest_reminder)) + @check_reminders.before_loop + async def before_check_reminders(self): + await self.bot.wait_until_ready() @app_commands.command( name="remindme", description="Reminds you after a certain amount of time.", ) async def remindme(self, interaction: discord.Interaction, time: str, *, reminder: str) -> None: - """ - Set a reminder for a certain amount of time. - - Parameters - ---------- - interaction : discord.Interaction - The discord interaction object. - time : str - Time in the format `[number][M/w/d/h/m/s]`. - reminder : str - Reminder content. - """ - seconds = convert_to_seconds(time) - # Check if the time is valid (this is set to 0 if the time is invalid via convert_to_seconds) if seconds == 0: await interaction.response.send_message( "Invalid time format. Please use the format `[number][M/w/d/h/m/s]`.", @@ -169,13 +84,13 @@ class RemindMe(commands.Cog): ) return - seconds = datetime.datetime.now(datetime.UTC) + datetime.timedelta(seconds=seconds) + expires_at = datetime.datetime.now(datetime.UTC) + datetime.timedelta(seconds=seconds) try: await self.db.insert_reminder( reminder_user_id=interaction.user.id, reminder_content=reminder, - reminder_expires_at=seconds, + reminder_expires_at=expires_at, reminder_channel_id=interaction.channel_id or 0, guild_id=interaction.guild_id or 0, ) @@ -186,12 +101,13 @@ class RemindMe(commands.Cog): user_name=interaction.user.name, user_display_avatar=interaction.user.display_avatar.url, title="Reminder Set", - description=f"Reminder set for .", + description=f"Reminder set for .", ) embed.add_field( name="Note", - value="If you have DMs closed, the reminder may not reach you. We will attempt to send it in this channel instead, however it is not guaranteed.", + value="- If you have DMs closed, we will attempt to send it in this channel instead, however it is not guaranteed.\n" + "- The reminder may be delayed by a few minutes due to the way Tux works.", ) except Exception as e: @@ -207,9 +123,6 @@ class RemindMe(commands.Cog): await interaction.response.send_message(embed=embed, ephemeral=True) - # Run update again to check if this reminder is the closest - await self.update() - async def setup(bot: Tux) -> None: await bot.add_cog(RemindMe(bot)) From 44cc360da82d7f70cf12946e60c5bad287d1c8b4 Mon Sep 17 00:00:00 2001 From: wlinator Date: Tue, 10 Sep 2024 09:35:20 -0400 Subject: [PATCH 05/38] Make the database fetch as light as possible, allowing a remindme every 120 seconds --- tux/cogs/utility/remindme.py | 14 ++++++-------- tux/database/controllers/reminder.py | 5 +++-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/tux/cogs/utility/remindme.py b/tux/cogs/utility/remindme.py index 53b64e1..249286d 100644 --- a/tux/cogs/utility/remindme.py +++ b/tux/cogs/utility/remindme.py @@ -19,16 +19,14 @@ class RemindMe(commands.Cog): self.db = DatabaseController().reminder self.check_reminders.start() - @tasks.loop(minutes=5) + @tasks.loop(seconds=120) async def check_reminders(self): - now = datetime.datetime.now(datetime.UTC) reminders = await self.db.get_unsent_reminders() for reminder in reminders: - if not reminder.reminder_sent and reminder.reminder_expires_at <= now: - await self.send_reminder(reminder) - await self.db.update_reminder_status(reminder.reminder_id, sent=True) - logger.debug(f'Status of reminder {reminder.reminder_id} updated to "sent".') + await self.send_reminder(reminder) + await self.db.update_reminder_status(reminder.reminder_id, sent=True) + logger.debug(f'Status of reminder {reminder.reminder_id} updated to "sent".') async def send_reminder(self, reminder: Reminder) -> None: user = self.bot.get_user(reminder.reminder_user_id) @@ -106,8 +104,8 @@ class RemindMe(commands.Cog): embed.add_field( name="Note", - value="- If you have DMs closed, we will attempt to send it in this channel instead, however it is not guaranteed.\n" - "- The reminder may be delayed by a few minutes due to the way Tux works.", + value="- If you have DMs closed, we will attempt to send it in this channel instead.\n" + "- The reminder may be delayed by up to 120 seconds due to the way Tux works.", ) except Exception as e: diff --git a/tux/database/controllers/reminder.py b/tux/database/controllers/reminder.py index 95f30f0..c4241a6 100644 --- a/tux/database/controllers/reminder.py +++ b/tux/database/controllers/reminder.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import UTC, datetime from prisma.models import Guild, Reminder from tux.database.client import db @@ -22,7 +22,8 @@ class ReminderController: return await self.table.find_first(where={"reminder_id": reminder_id}) async def get_unsent_reminders(self) -> list[Reminder]: - return await self.table.find_many(where={"reminder_sent": False}) + now = datetime.now(UTC) + return await self.table.find_many(where={"reminder_sent": False, "reminder_expires_at": {"lte": now}}) async def insert_reminder( self, From 8f7c7e76a529ea21cf79c2b754eb1ac1d3be0e50 Mon Sep 17 00:00:00 2001 From: wlinator Date: Tue, 10 Sep 2024 09:52:59 -0400 Subject: [PATCH 06/38] Update error logging for failed reminders --- tux/cogs/utility/remindme.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tux/cogs/utility/remindme.py b/tux/cogs/utility/remindme.py index 249286d..5d98483 100644 --- a/tux/cogs/utility/remindme.py +++ b/tux/cogs/utility/remindme.py @@ -57,11 +57,13 @@ class RemindMe(commands.Cog): else: logger.error( - f"Failed to send reminder to {user.id}, DMs closed and channel not found.", + f"Failed to send reminder {reminder.reminder_id}, DMs closed and channel not found.", ) else: - logger.error(f"Failed to send reminder to {reminder.reminder_user_id}, user not found.") + logger.error( + f"Failed to send reminder {reminder.reminder_id}, user with ID {reminder.reminder_user_id} not found.", + ) @check_reminders.before_loop async def before_check_reminders(self): From 5578504255e1893ce738055369ec396b6f24dd56 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 05:00:37 +0000 Subject: [PATCH 07/38] fix(deps): update dependency githubkit to v0.11.10 --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index e090a35..b020dd7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -811,13 +811,13 @@ dev = ["flake8", "markdown", "twine", "wheel"] [[package]] name = "githubkit" -version = "0.11.9" +version = "0.11.10" description = "GitHub SDK for Python" optional = false python-versions = "<4.0,>=3.8" files = [ - {file = "githubkit-0.11.9-py3-none-any.whl", hash = "sha256:68933ff9867467418c90176e2ea77c7623fa7ab8fbf64723c2ca56c09b14a0f6"}, - {file = "githubkit-0.11.9.tar.gz", hash = "sha256:d25da1a667cce32f3d1ab0f33cf52fa328a55e85a6916e19da2765b0602c472a"}, + {file = "githubkit-0.11.10-py3-none-any.whl", hash = "sha256:e95074916af4a5f357d47005022a555984e16729d9e6204161ef67bc29a78789"}, + {file = "githubkit-0.11.10.tar.gz", hash = "sha256:9729783ba44935ca47b3bebaad76971ccc3d3b548b8af54e06d16a489b0ece90"}, ] [package.dependencies] From c2d6a6db5c0caeb4daea4a651ac9bfb6c6f58935 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 20:31:38 +0000 Subject: [PATCH 08/38] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/gitleaks/gitleaks: v8.18.4 → v8.19.2](https://github.com/gitleaks/gitleaks/compare/v8.18.4...v8.19.2) - [github.com/astral-sh/ruff-pre-commit: v0.6.4 → v0.6.5](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.4...v0.6.5) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b5f9238..baef465 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/gitleaks/gitleaks - rev: v8.18.4 + rev: v8.19.2 hooks: - id: gitleaks @@ -12,7 +12,7 @@ repos: - id: check-toml - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.4 + rev: v0.6.5 hooks: # Run the linter. - id: ruff From f7c5d7f4c2253d1548dc3fc81ea742f6193b6ac6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 17 Sep 2024 16:36:36 +0000 Subject: [PATCH 09/38] fix(deps): update dependency pydantic to v2.9.2 --- poetry.lock | 188 ++++++++++++++++++++++++++-------------------------- 1 file changed, 94 insertions(+), 94 deletions(-) diff --git a/poetry.lock b/poetry.lock index e090a35..c898222 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1556,18 +1556,18 @@ files = [ [[package]] name = "pydantic" -version = "2.9.1" +version = "2.9.2" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.9.1-py3-none-any.whl", hash = "sha256:7aff4db5fdf3cf573d4b3c30926a510a10e19a0774d38fc4967f78beb6deb612"}, - {file = "pydantic-2.9.1.tar.gz", hash = "sha256:1363c7d975c7036df0db2b4a61f2e062fbc0aa5ab5f2772e0ffc7191a4f4bce2"}, + {file = "pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12"}, + {file = "pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f"}, ] [package.dependencies] annotated-types = ">=0.6.0" -pydantic-core = "2.23.3" +pydantic-core = "2.23.4" typing-extensions = [ {version = ">=4.12.2", markers = "python_version >= \"3.13\""}, {version = ">=4.6.1", markers = "python_version < \"3.13\""}, @@ -1579,100 +1579,100 @@ timezone = ["tzdata"] [[package]] name = "pydantic-core" -version = "2.23.3" +version = "2.23.4" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.23.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:7f10a5d1b9281392f1bf507d16ac720e78285dfd635b05737c3911637601bae6"}, - {file = "pydantic_core-2.23.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3c09a7885dd33ee8c65266e5aa7fb7e2f23d49d8043f089989726391dd7350c5"}, - {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6470b5a1ec4d1c2e9afe928c6cb37eb33381cab99292a708b8cb9aa89e62429b"}, - {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9172d2088e27d9a185ea0a6c8cebe227a9139fd90295221d7d495944d2367700"}, - {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86fc6c762ca7ac8fbbdff80d61b2c59fb6b7d144aa46e2d54d9e1b7b0e780e01"}, - {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0cb80fd5c2df4898693aa841425ea1727b1b6d2167448253077d2a49003e0ed"}, - {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03667cec5daf43ac4995cefa8aaf58f99de036204a37b889c24a80927b629cec"}, - {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:047531242f8e9c2db733599f1c612925de095e93c9cc0e599e96cf536aaf56ba"}, - {file = "pydantic_core-2.23.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5499798317fff7f25dbef9347f4451b91ac2a4330c6669821c8202fd354c7bee"}, - {file = "pydantic_core-2.23.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bbb5e45eab7624440516ee3722a3044b83fff4c0372efe183fd6ba678ff681fe"}, - {file = "pydantic_core-2.23.3-cp310-none-win32.whl", hash = "sha256:8b5b3ed73abb147704a6e9f556d8c5cb078f8c095be4588e669d315e0d11893b"}, - {file = "pydantic_core-2.23.3-cp310-none-win_amd64.whl", hash = "sha256:2b603cde285322758a0279995b5796d64b63060bfbe214b50a3ca23b5cee3e83"}, - {file = "pydantic_core-2.23.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:c889fd87e1f1bbeb877c2ee56b63bb297de4636661cc9bbfcf4b34e5e925bc27"}, - {file = "pydantic_core-2.23.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea85bda3189fb27503af4c45273735bcde3dd31c1ab17d11f37b04877859ef45"}, - {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7f7f72f721223f33d3dc98a791666ebc6a91fa023ce63733709f4894a7dc611"}, - {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b2b55b0448e9da68f56b696f313949cda1039e8ec7b5d294285335b53104b61"}, - {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c24574c7e92e2c56379706b9a3f07c1e0c7f2f87a41b6ee86653100c4ce343e5"}, - {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2b05e6ccbee333a8f4b8f4d7c244fdb7a979e90977ad9c51ea31261e2085ce0"}, - {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2c409ce1c219c091e47cb03feb3c4ed8c2b8e004efc940da0166aaee8f9d6c8"}, - {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d965e8b325f443ed3196db890d85dfebbb09f7384486a77461347f4adb1fa7f8"}, - {file = "pydantic_core-2.23.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f56af3a420fb1ffaf43ece3ea09c2d27c444e7c40dcb7c6e7cf57aae764f2b48"}, - {file = "pydantic_core-2.23.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5b01a078dd4f9a52494370af21aa52964e0a96d4862ac64ff7cea06e0f12d2c5"}, - {file = "pydantic_core-2.23.3-cp311-none-win32.whl", hash = "sha256:560e32f0df04ac69b3dd818f71339983f6d1f70eb99d4d1f8e9705fb6c34a5c1"}, - {file = "pydantic_core-2.23.3-cp311-none-win_amd64.whl", hash = "sha256:c744fa100fdea0d000d8bcddee95213d2de2e95b9c12be083370b2072333a0fa"}, - {file = "pydantic_core-2.23.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:e0ec50663feedf64d21bad0809f5857bac1ce91deded203efc4a84b31b2e4305"}, - {file = "pydantic_core-2.23.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:db6e6afcb95edbe6b357786684b71008499836e91f2a4a1e55b840955b341dbb"}, - {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98ccd69edcf49f0875d86942f4418a4e83eb3047f20eb897bffa62a5d419c8fa"}, - {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a678c1ac5c5ec5685af0133262103defb427114e62eafeda12f1357a12140162"}, - {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01491d8b4d8db9f3391d93b0df60701e644ff0894352947f31fff3e52bd5c801"}, - {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fcf31facf2796a2d3b7fe338fe8640aa0166e4e55b4cb108dbfd1058049bf4cb"}, - {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7200fd561fb3be06827340da066df4311d0b6b8eb0c2116a110be5245dceb326"}, - {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dc1636770a809dee2bd44dd74b89cc80eb41172bcad8af75dd0bc182c2666d4c"}, - {file = "pydantic_core-2.23.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:67a5def279309f2e23014b608c4150b0c2d323bd7bccd27ff07b001c12c2415c"}, - {file = "pydantic_core-2.23.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:748bdf985014c6dd3e1e4cc3db90f1c3ecc7246ff5a3cd4ddab20c768b2f1dab"}, - {file = "pydantic_core-2.23.3-cp312-none-win32.whl", hash = "sha256:255ec6dcb899c115f1e2a64bc9ebc24cc0e3ab097775755244f77360d1f3c06c"}, - {file = "pydantic_core-2.23.3-cp312-none-win_amd64.whl", hash = "sha256:40b8441be16c1e940abebed83cd006ddb9e3737a279e339dbd6d31578b802f7b"}, - {file = "pydantic_core-2.23.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:6daaf5b1ba1369a22c8b050b643250e3e5efc6a78366d323294aee54953a4d5f"}, - {file = "pydantic_core-2.23.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d015e63b985a78a3d4ccffd3bdf22b7c20b3bbd4b8227809b3e8e75bc37f9cb2"}, - {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3fc572d9b5b5cfe13f8e8a6e26271d5d13f80173724b738557a8c7f3a8a3791"}, - {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f6bd91345b5163ee7448bee201ed7dd601ca24f43f439109b0212e296eb5b423"}, - {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc379c73fd66606628b866f661e8785088afe2adaba78e6bbe80796baf708a63"}, - {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbdce4b47592f9e296e19ac31667daed8753c8367ebb34b9a9bd89dacaa299c9"}, - {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc3cf31edf405a161a0adad83246568647c54404739b614b1ff43dad2b02e6d5"}, - {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8e22b477bf90db71c156f89a55bfe4d25177b81fce4aa09294d9e805eec13855"}, - {file = "pydantic_core-2.23.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:0a0137ddf462575d9bce863c4c95bac3493ba8e22f8c28ca94634b4a1d3e2bb4"}, - {file = "pydantic_core-2.23.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:203171e48946c3164fe7691fc349c79241ff8f28306abd4cad5f4f75ed80bc8d"}, - {file = "pydantic_core-2.23.3-cp313-none-win32.whl", hash = "sha256:76bdab0de4acb3f119c2a4bff740e0c7dc2e6de7692774620f7452ce11ca76c8"}, - {file = "pydantic_core-2.23.3-cp313-none-win_amd64.whl", hash = "sha256:37ba321ac2a46100c578a92e9a6aa33afe9ec99ffa084424291d84e456f490c1"}, - {file = "pydantic_core-2.23.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d063c6b9fed7d992bcbebfc9133f4c24b7a7f215d6b102f3e082b1117cddb72c"}, - {file = "pydantic_core-2.23.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6cb968da9a0746a0cf521b2b5ef25fc5a0bee9b9a1a8214e0a1cfaea5be7e8a4"}, - {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edbefe079a520c5984e30e1f1f29325054b59534729c25b874a16a5048028d16"}, - {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cbaaf2ef20d282659093913da9d402108203f7cb5955020bd8d1ae5a2325d1c4"}, - {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fb539d7e5dc4aac345846f290cf504d2fd3c1be26ac4e8b5e4c2b688069ff4cf"}, - {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e6f33503c5495059148cc486867e1d24ca35df5fc064686e631e314d959ad5b"}, - {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:04b07490bc2f6f2717b10c3969e1b830f5720b632f8ae2f3b8b1542394c47a8e"}, - {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:03795b9e8a5d7fda05f3873efc3f59105e2dcff14231680296b87b80bb327295"}, - {file = "pydantic_core-2.23.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c483dab0f14b8d3f0df0c6c18d70b21b086f74c87ab03c59250dbf6d3c89baba"}, - {file = "pydantic_core-2.23.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8b2682038e255e94baf2c473dca914a7460069171ff5cdd4080be18ab8a7fd6e"}, - {file = "pydantic_core-2.23.3-cp38-none-win32.whl", hash = "sha256:f4a57db8966b3a1d1a350012839c6a0099f0898c56512dfade8a1fe5fb278710"}, - {file = "pydantic_core-2.23.3-cp38-none-win_amd64.whl", hash = "sha256:13dd45ba2561603681a2676ca56006d6dee94493f03d5cadc055d2055615c3ea"}, - {file = "pydantic_core-2.23.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:82da2f4703894134a9f000e24965df73cc103e31e8c31906cc1ee89fde72cbd8"}, - {file = "pydantic_core-2.23.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dd9be0a42de08f4b58a3cc73a123f124f65c24698b95a54c1543065baca8cf0e"}, - {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89b731f25c80830c76fdb13705c68fef6a2b6dc494402987c7ea9584fe189f5d"}, - {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c6de1ec30c4bb94f3a69c9f5f2182baeda5b809f806676675e9ef6b8dc936f28"}, - {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb68b41c3fa64587412b104294b9cbb027509dc2f6958446c502638d481525ef"}, - {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c3980f2843de5184656aab58698011b42763ccba11c4a8c35936c8dd6c7068c"}, - {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94f85614f2cba13f62c3c6481716e4adeae48e1eaa7e8bac379b9d177d93947a"}, - {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:510b7fb0a86dc8f10a8bb43bd2f97beb63cffad1203071dc434dac26453955cd"}, - {file = "pydantic_core-2.23.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1eba2f7ce3e30ee2170410e2171867ea73dbd692433b81a93758ab2de6c64835"}, - {file = "pydantic_core-2.23.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4b259fd8409ab84b4041b7b3f24dcc41e4696f180b775961ca8142b5b21d0e70"}, - {file = "pydantic_core-2.23.3-cp39-none-win32.whl", hash = "sha256:40d9bd259538dba2f40963286009bf7caf18b5112b19d2b55b09c14dde6db6a7"}, - {file = "pydantic_core-2.23.3-cp39-none-win_amd64.whl", hash = "sha256:5a8cd3074a98ee70173a8633ad3c10e00dcb991ecec57263aacb4095c5efb958"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f399e8657c67313476a121a6944311fab377085ca7f490648c9af97fc732732d"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:6b5547d098c76e1694ba85f05b595720d7c60d342f24d5aad32c3049131fa5c4"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0dda0290a6f608504882d9f7650975b4651ff91c85673341789a476b1159f211"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65b6e5da855e9c55a0c67f4db8a492bf13d8d3316a59999cfbaf98cc6e401961"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:09e926397f392059ce0afdcac920df29d9c833256354d0c55f1584b0b70cf07e"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:87cfa0ed6b8c5bd6ae8b66de941cece179281239d482f363814d2b986b79cedc"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e61328920154b6a44d98cabcb709f10e8b74276bc709c9a513a8c37a18786cc4"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ce3317d155628301d649fe5e16a99528d5680af4ec7aa70b90b8dacd2d725c9b"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e89513f014c6be0d17b00a9a7c81b1c426f4eb9224b15433f3d98c1a071f8433"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:4f62c1c953d7ee375df5eb2e44ad50ce2f5aff931723b398b8bc6f0ac159791a"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2718443bc671c7ac331de4eef9b673063b10af32a0bb385019ad61dcf2cc8f6c"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0d90e08b2727c5d01af1b5ef4121d2f0c99fbee692c762f4d9d0409c9da6541"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2b676583fc459c64146debea14ba3af54e540b61762dfc0613dc4e98c3f66eeb"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:50e4661f3337977740fdbfbae084ae5693e505ca2b3130a6d4eb0f2281dc43b8"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:68f4cf373f0de6abfe599a38307f4417c1c867ca381c03df27c873a9069cda25"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:59d52cf01854cb26c46958552a21acb10dd78a52aa34c86f284e66b209db8cab"}, - {file = "pydantic_core-2.23.3.tar.gz", hash = "sha256:3cb0f65d8b4121c1b015c60104a685feb929a29d7cf204387c7f2688c7974690"}, + {file = "pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b"}, + {file = "pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f"}, + {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3"}, + {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071"}, + {file = "pydantic_core-2.23.4-cp310-none-win32.whl", hash = "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119"}, + {file = "pydantic_core-2.23.4-cp310-none-win_amd64.whl", hash = "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f"}, + {file = "pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8"}, + {file = "pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b"}, + {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0"}, + {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64"}, + {file = "pydantic_core-2.23.4-cp311-none-win32.whl", hash = "sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f"}, + {file = "pydantic_core-2.23.4-cp311-none-win_amd64.whl", hash = "sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3"}, + {file = "pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231"}, + {file = "pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126"}, + {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e"}, + {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24"}, + {file = "pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84"}, + {file = "pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9"}, + {file = "pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc"}, + {file = "pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327"}, + {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6"}, + {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f"}, + {file = "pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769"}, + {file = "pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5"}, + {file = "pydantic_core-2.23.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d4488a93b071c04dc20f5cecc3631fc78b9789dd72483ba15d423b5b3689b555"}, + {file = "pydantic_core-2.23.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:81965a16b675b35e1d09dd14df53f190f9129c0202356ed44ab2728b1c905658"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffa2ebd4c8530079140dd2d7f794a9d9a73cbb8e9d59ffe24c63436efa8f271"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:61817945f2fe7d166e75fbfb28004034b48e44878177fc54d81688e7b85a3665"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29d2c342c4bc01b88402d60189f3df065fb0dda3654744d5a165a5288a657368"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e11661ce0fd30a6790e8bcdf263b9ec5988e95e63cf901972107efc49218b13"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d18368b137c6295db49ce7218b1a9ba15c5bc254c96d7c9f9e924a9bc7825ad"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec4e55f79b1c4ffb2eecd8a0cfba9955a2588497d96851f4c8f99aa4a1d39b12"}, + {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:374a5e5049eda9e0a44c696c7ade3ff355f06b1fe0bb945ea3cac2bc336478a2"}, + {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5c364564d17da23db1106787675fc7af45f2f7b58b4173bfdd105564e132e6fb"}, + {file = "pydantic_core-2.23.4-cp38-none-win32.whl", hash = "sha256:d7a80d21d613eec45e3d41eb22f8f94ddc758a6c4720842dc74c0581f54993d6"}, + {file = "pydantic_core-2.23.4-cp38-none-win_amd64.whl", hash = "sha256:5f5ff8d839f4566a474a969508fe1c5e59c31c80d9e140566f9a37bba7b8d556"}, + {file = "pydantic_core-2.23.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a4fa4fc04dff799089689f4fd502ce7d59de529fc2f40a2c8836886c03e0175a"}, + {file = "pydantic_core-2.23.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a7df63886be5e270da67e0966cf4afbae86069501d35c8c1b3b6c168f42cb36"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcedcd19a557e182628afa1d553c3895a9f825b936415d0dbd3cd0bbcfd29b4b"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f54b118ce5de9ac21c363d9b3caa6c800341e8c47a508787e5868c6b79c9323"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86d2f57d3e1379a9525c5ab067b27dbb8a0642fb5d454e17a9ac434f9ce523e3"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de6d1d1b9e5101508cb37ab0d972357cac5235f5c6533d1071964c47139257df"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1278e0d324f6908e872730c9102b0112477a7f7cf88b308e4fc36ce1bdb6d58c"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a6b5099eeec78827553827f4c6b8615978bb4b6a88e5d9b93eddf8bb6790f55"}, + {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e55541f756f9b3ee346b840103f32779c695a19826a4c442b7954550a0972040"}, + {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5c7ba8ffb6d6f8f2ab08743be203654bb1aaa8c9dcb09f82ddd34eadb695605"}, + {file = "pydantic_core-2.23.4-cp39-none-win32.whl", hash = "sha256:37b0fe330e4a58d3c58b24d91d1eb102aeec675a3db4c292ec3928ecd892a9a6"}, + {file = "pydantic_core-2.23.4-cp39-none-win_amd64.whl", hash = "sha256:1498bec4c05c9c787bde9125cfdcc63a41004ff167f495063191b863399b1a29"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:78ddaaa81421a29574a682b3179d4cf9e6d405a09b99d93ddcf7e5239c742e21"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:883a91b5dd7d26492ff2f04f40fbb652de40fcc0afe07e8129e8ae779c2110eb"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88ad334a15b32a791ea935af224b9de1bf99bcd62fabf745d5f3442199d86d59"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:233710f069d251feb12a56da21e14cca67994eab08362207785cf8c598e74577"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19442362866a753485ba5e4be408964644dd6a09123d9416c54cd49171f50744"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:624e278a7d29b6445e4e813af92af37820fafb6dcc55c012c834f9e26f9aaaef"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5ef8f42bec47f21d07668a043f077d507e5bf4e668d5c6dfe6aaba89de1a5b8"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:aea443fffa9fbe3af1a9ba721a87f926fe548d32cab71d188a6ede77d0ff244e"}, + {file = "pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863"}, ] [package.dependencies] From 09c79b9eae6deddfb56f001d454e4e6d3814193a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2024 09:35:01 +0000 Subject: [PATCH 10/38] chore(deps): update dependency mkdocs-material to v9.5.35 --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index e090a35..6768294 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1160,13 +1160,13 @@ pyyaml = ">=5.1" [[package]] name = "mkdocs-material" -version = "9.5.34" +version = "9.5.35" description = "Documentation that simply works" optional = false python-versions = ">=3.8" files = [ - {file = "mkdocs_material-9.5.34-py3-none-any.whl", hash = "sha256:54caa8be708de2b75167fd4d3b9f3d949579294f49cb242515d4653dbee9227e"}, - {file = "mkdocs_material-9.5.34.tar.gz", hash = "sha256:1e60ddf716cfb5679dfd65900b8a25d277064ed82d9a53cd5190e3f894df7840"}, + {file = "mkdocs_material-9.5.35-py3-none-any.whl", hash = "sha256:44e069d87732d29f4a2533ae0748fa0e67e270043270c71f04d0fba11a357b24"}, + {file = "mkdocs_material-9.5.35.tar.gz", hash = "sha256:0d233d7db067ac896bf22ee7950eebf2b1eaf26c155bb27382bf4174021cc117"}, ] [package.dependencies] From 7ec3993429559a44a8c690125b14ddb27fdca1eb Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2024 09:35:11 +0000 Subject: [PATCH 11/38] fix(deps): update dependency pyright to v1.1.381 --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index e090a35..ec2e873 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1758,13 +1758,13 @@ tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] [[package]] name = "pyright" -version = "1.1.380" +version = "1.1.381" description = "Command line wrapper for pyright" optional = false python-versions = ">=3.7" files = [ - {file = "pyright-1.1.380-py3-none-any.whl", hash = "sha256:a6404392053d8848bacc7aebcbd9d318bb46baf1a1a000359305481920f43879"}, - {file = "pyright-1.1.380.tar.gz", hash = "sha256:e6ceb1a5f7e9f03106e0aa1d6fbb4d97735a5e7ffb59f3de6b2db590baf935b2"}, + {file = "pyright-1.1.381-py3-none-any.whl", hash = "sha256:5dc0aa80a265675d36abab59c674ae01dbe476714f91845b61b841d34aa99081"}, + {file = "pyright-1.1.381.tar.gz", hash = "sha256:314cf0c1351c189524fb10c7ac20688ecd470e8cc505c394d642c9c80bf7c3a5"}, ] [package.dependencies] From b21e2f641c2bbba07498799d7fbc2b3d400c528f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 20 Sep 2024 07:47:55 +0000 Subject: [PATCH 12/38] fix(deps): update dependency ruff to v0.6.6 --- poetry.lock | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/poetry.lock b/poetry.lock index e090a35..afea086 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2043,29 +2043,29 @@ pyasn1 = ">=0.1.3" [[package]] name = "ruff" -version = "0.6.5" +version = "0.6.6" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.6.5-py3-none-linux_armv6l.whl", hash = "sha256:7e4e308f16e07c95fc7753fc1aaac690a323b2bb9f4ec5e844a97bb7fbebd748"}, - {file = "ruff-0.6.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:932cd69eefe4daf8c7d92bd6689f7e8182571cb934ea720af218929da7bd7d69"}, - {file = "ruff-0.6.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3a8d42d11fff8d3143ff4da41742a98f8f233bf8890e9fe23077826818f8d680"}, - {file = "ruff-0.6.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a50af6e828ee692fb10ff2dfe53f05caecf077f4210fae9677e06a808275754f"}, - {file = "ruff-0.6.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:794ada3400a0d0b89e3015f1a7e01f4c97320ac665b7bc3ade24b50b54cb2972"}, - {file = "ruff-0.6.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:381413ec47f71ce1d1c614f7779d88886f406f1fd53d289c77e4e533dc6ea200"}, - {file = "ruff-0.6.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:52e75a82bbc9b42e63c08d22ad0ac525117e72aee9729a069d7c4f235fc4d276"}, - {file = "ruff-0.6.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09c72a833fd3551135ceddcba5ebdb68ff89225d30758027280968c9acdc7810"}, - {file = "ruff-0.6.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:800c50371bdcb99b3c1551d5691e14d16d6f07063a518770254227f7f6e8c178"}, - {file = "ruff-0.6.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e25ddd9cd63ba1f3bd51c1f09903904a6adf8429df34f17d728a8fa11174253"}, - {file = "ruff-0.6.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:7291e64d7129f24d1b0c947ec3ec4c0076e958d1475c61202497c6aced35dd19"}, - {file = "ruff-0.6.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:9ad7dfbd138d09d9a7e6931e6a7e797651ce29becd688be8a0d4d5f8177b4b0c"}, - {file = "ruff-0.6.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:005256d977021790cc52aa23d78f06bb5090dc0bfbd42de46d49c201533982ae"}, - {file = "ruff-0.6.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:482c1e6bfeb615eafc5899127b805d28e387bd87db38b2c0c41d271f5e58d8cc"}, - {file = "ruff-0.6.5-py3-none-win32.whl", hash = "sha256:cf4d3fa53644137f6a4a27a2b397381d16454a1566ae5335855c187fbf67e4f5"}, - {file = "ruff-0.6.5-py3-none-win_amd64.whl", hash = "sha256:3e42a57b58e3612051a636bc1ac4e6b838679530235520e8f095f7c44f706ff9"}, - {file = "ruff-0.6.5-py3-none-win_arm64.whl", hash = "sha256:51935067740773afdf97493ba9b8231279e9beef0f2a8079188c4776c25688e0"}, - {file = "ruff-0.6.5.tar.gz", hash = "sha256:4d32d87fab433c0cf285c3683dd4dae63be05fd7a1d65b3f5bf7cdd05a6b96fb"}, + {file = "ruff-0.6.6-py3-none-linux_armv6l.whl", hash = "sha256:f5bc5398457484fc0374425b43b030e4668ed4d2da8ee7fdda0e926c9f11ccfb"}, + {file = "ruff-0.6.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:515a698254c9c47bb84335281a170213b3ee5eb47feebe903e1be10087a167ce"}, + {file = "ruff-0.6.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6bb1b4995775f1837ab70f26698dd73852bbb82e8f70b175d2713c0354fe9182"}, + {file = "ruff-0.6.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69c546f412dfae8bb9cc4f27f0e45cdd554e42fecbb34f03312b93368e1cd0a6"}, + {file = "ruff-0.6.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:59627e97364329e4eae7d86fa7980c10e2b129e2293d25c478ebcb861b3e3fd6"}, + {file = "ruff-0.6.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94c3f78c3d32190aafbb6bc5410c96cfed0a88aadb49c3f852bbc2aa9783a7d8"}, + {file = "ruff-0.6.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:704da526c1e137f38c8a067a4a975fe6834b9f8ba7dbc5fd7503d58148851b8f"}, + {file = "ruff-0.6.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:efeede5815a24104579a0f6320660536c5ffc1c91ae94f8c65659af915fb9de9"}, + {file = "ruff-0.6.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e368aef0cc02ca3593eae2fb8186b81c9c2b3f39acaaa1108eb6b4d04617e61f"}, + {file = "ruff-0.6.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2653fc3b2a9315bd809725c88dd2446550099728d077a04191febb5ea79a4f79"}, + {file = "ruff-0.6.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:bb858cd9ce2d062503337c5b9784d7b583bcf9d1a43c4df6ccb5eab774fbafcb"}, + {file = "ruff-0.6.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:488f8e15c01ea9afb8c0ba35d55bd951f484d0c1b7c5fd746ce3c47ccdedce68"}, + {file = "ruff-0.6.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:aefb0bd15f1cfa4c9c227b6120573bb3d6c4ee3b29fb54a5ad58f03859bc43c6"}, + {file = "ruff-0.6.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a4c0698cc780bcb2c61496cbd56b6a3ac0ad858c966652f7dbf4ceb029252fbe"}, + {file = "ruff-0.6.6-py3-none-win32.whl", hash = "sha256:aadf81ddc8ab5b62da7aae78a91ec933cbae9f8f1663ec0325dae2c364e4ad84"}, + {file = "ruff-0.6.6-py3-none-win_amd64.whl", hash = "sha256:0adb801771bc1f1b8cf4e0a6fdc30776e7c1894810ff3b344e50da82ef50eeb1"}, + {file = "ruff-0.6.6-py3-none-win_arm64.whl", hash = "sha256:4b4d32c137bc781c298964dd4e52f07d6f7d57c03eae97a72d97856844aa510a"}, + {file = "ruff-0.6.6.tar.gz", hash = "sha256:0fc030b6fd14814d69ac0196396f6761921bd20831725c7361e1b8100b818034"}, ] [[package]] From 6e201bafd5548cc2a9c5b0328a0813e7cac6b7ce Mon Sep 17 00:00:00 2001 From: kzndotsh Date: Sat, 21 Sep 2024 00:39:08 -0400 Subject: [PATCH 13/38] fix(remindme.py): wrap reminder sending logic in try-except block to handle exceptions and prevent app crash feat(remindme.py): add logging of errors when sending reminders fails to improve error tracking and debugging --- tux/cogs/utility/remindme.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tux/cogs/utility/remindme.py b/tux/cogs/utility/remindme.py index 5d98483..dd59116 100644 --- a/tux/cogs/utility/remindme.py +++ b/tux/cogs/utility/remindme.py @@ -23,10 +23,14 @@ class RemindMe(commands.Cog): async def check_reminders(self): reminders = await self.db.get_unsent_reminders() - for reminder in reminders: - await self.send_reminder(reminder) - await self.db.update_reminder_status(reminder.reminder_id, sent=True) - logger.debug(f'Status of reminder {reminder.reminder_id} updated to "sent".') + try: + for reminder in reminders: + await self.send_reminder(reminder) + await self.db.update_reminder_status(reminder.reminder_id, sent=True) + logger.debug(f'Status of reminder {reminder.reminder_id} updated to "sent".') + + except Exception as e: + logger.error(f"Error sending reminders: {e}") async def send_reminder(self, reminder: Reminder) -> None: user = self.bot.get_user(reminder.reminder_user_id) From cb135e4468cfedf847b1c796c31a51f09a96c9f9 Mon Sep 17 00:00:00 2001 From: kzndotsh Date: Sat, 21 Sep 2024 00:40:54 -0400 Subject: [PATCH 14/38] chore(.pre-commit-config.yaml): upgrade ruff-pre-commit from v0.6.5 to v0.6.6 to include latest updates and bug fixes --- .pre-commit-config.yaml | 2 +- poetry.lock | 52 ++++++++++++++++++++--------------------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index baef465..8ca57a0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,7 +12,7 @@ repos: - id: check-toml - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.5 + rev: v0.6.6 hooks: # Run the linter. - id: ruff diff --git a/poetry.lock b/poetry.lock index 8bbe58b..ca48a22 100644 --- a/poetry.lock +++ b/poetry.lock @@ -190,13 +190,13 @@ files = [ [[package]] name = "anyio" -version = "4.4.0" +version = "4.5.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.8" files = [ - {file = "anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7"}, - {file = "anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94"}, + {file = "anyio-4.5.0-py3-none-any.whl", hash = "sha256:fdeb095b7cc5a5563175eedd926ec4ae55413bb4be5770c424af0ba46ccb4a78"}, + {file = "anyio-4.5.0.tar.gz", hash = "sha256:c5a275fe5ca0afd788001f58fca1e69e29ce706d746e317d660e21f70c530ef9"}, ] [package.dependencies] @@ -204,9 +204,9 @@ idna = ">=2.8" sniffio = ">=1.1" [package.extras] -doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] -trio = ["trio (>=0.23)"] +doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.21.0b1)"] +trio = ["trio (>=0.26.1)"] [[package]] name = "astunparse" @@ -692,18 +692,18 @@ files = [ [[package]] name = "filelock" -version = "3.16.0" +version = "3.16.1" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.16.0-py3-none-any.whl", hash = "sha256:f6ed4c963184f4c84dd5557ce8fece759a3724b37b80c6c4f20a2f63a4dc6609"}, - {file = "filelock-3.16.0.tar.gz", hash = "sha256:81de9eb8453c769b63369f87f11131a7ab04e367f8d97ad39dc230daa07e3bec"}, + {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, + {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, ] [package.extras] -docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.1.1)", "pytest (>=8.3.2)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.3)"] +docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] typing = ["typing-extensions (>=4.12.2)"] [[package]] @@ -914,13 +914,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "identify" -version = "2.6.0" +version = "2.6.1" description = "File identification library for Python" optional = false python-versions = ">=3.8" files = [ - {file = "identify-2.6.0-py2.py3-none-any.whl", hash = "sha256:e79ae4406387a9d300332b5fd366d8994f1525e8414984e1a59e058b2eda2dd0"}, - {file = "identify-2.6.0.tar.gz", hash = "sha256:cb171c685bdc31bcc4c1734698736a7d5b6c8bf2e0c15117f4d469c8640ae5cf"}, + {file = "identify-2.6.1-py2.py3-none-any.whl", hash = "sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0"}, + {file = "identify-2.6.1.tar.gz", hash = "sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98"}, ] [package.extras] @@ -928,13 +928,13 @@ license = ["ukkonen"] [[package]] name = "idna" -version = "3.9" +version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" files = [ - {file = "idna-3.9-py3-none-any.whl", hash = "sha256:69297d5da0cc9281c77efffb4e730254dd45943f45bbfb461de5991713989b1e"}, - {file = "idna-3.9.tar.gz", hash = "sha256:e5c5dafde284f26e9e0f28f6ea2d6400abd5ca099864a67f576f3981c6476124"}, + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, ] [package.extras] @@ -1446,13 +1446,13 @@ xmp = ["defusedxml"] [[package]] name = "platformdirs" -version = "4.3.3" +version = "4.3.6" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.3.3-py3-none-any.whl", hash = "sha256:50a5450e2e84f44539718293cbb1da0a0885c9d14adf21b77bae4e66fc99d9b5"}, - {file = "platformdirs-4.3.3.tar.gz", hash = "sha256:d4e0b7d8ec176b341fb03cb11ca12d0276faa8c485f9cd218f613840463fc2c0"}, + {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, + {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, ] [package.extras] @@ -2218,13 +2218,13 @@ files = [ [[package]] name = "types-pyyaml" -version = "6.0.12.20240808" +version = "6.0.12.20240917" description = "Typing stubs for PyYAML" optional = false python-versions = ">=3.8" files = [ - {file = "types-PyYAML-6.0.12.20240808.tar.gz", hash = "sha256:b8f76ddbd7f65440a8bda5526a9607e4c7a322dc2f8e1a8c405644f9a6f4b9af"}, - {file = "types_PyYAML-6.0.12.20240808-py3-none-any.whl", hash = "sha256:deda34c5c655265fc517b546c902aa6eed2ef8d3e921e4765fe606fe2afe8d35"}, + {file = "types-PyYAML-6.0.12.20240917.tar.gz", hash = "sha256:d1405a86f9576682234ef83bcb4e6fff7c9305c8b1fbad5e0bcd4f7dbdc9c587"}, + {file = "types_PyYAML-6.0.12.20240917-py3-none-any.whl", hash = "sha256:392b267f1c0fe6022952462bf5d6523f31e37f6cea49b14cee7ad634b6301570"}, ] [[package]] @@ -2285,13 +2285,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" -version = "20.26.4" +version = "20.26.5" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.26.4-py3-none-any.whl", hash = "sha256:48f2695d9809277003f30776d155615ffc11328e6a0a8c1f0ec80188d7874a55"}, - {file = "virtualenv-20.26.4.tar.gz", hash = "sha256:c17f4e0f3e6036e9f26700446f85c76ab11df65ff6d8a9cbfad9f71aabfcf23c"}, + {file = "virtualenv-20.26.5-py3-none-any.whl", hash = "sha256:4f3ac17b81fba3ce3bd6f4ead2749a72da5929c01774948e243db9ba41df4ff6"}, + {file = "virtualenv-20.26.5.tar.gz", hash = "sha256:ce489cac131aa58f4b25e321d6d186171f78e6cb13fafbf32a840cee67733ff4"}, ] [package.dependencies] From edb0b5b29bbe7d96d95029de6552b1e5ff6203f2 Mon Sep 17 00:00:00 2001 From: kzndotsh Date: Sat, 21 Sep 2024 01:14:32 -0400 Subject: [PATCH 15/38] refactor: convert methods to static methods in various classes for improved clarity and usability --- tux/cogs/admin/mail.py | 8 ++- tux/cogs/fun/imgeffect.py | 116 +++++++++++++++++--------------- tux/cogs/fun/random.py | 8 ++- tux/cogs/info/info.py | 9 +-- tux/cogs/moderation/__init__.py | 2 +- tux/cogs/moderation/cases.py | 13 ++-- tux/cogs/moderation/jail.py | 2 +- tux/cogs/moderation/slowmode.py | 11 +-- tux/cogs/services/bookmarks.py | 4 +- tux/cogs/utility/run.py | 10 +-- tux/cogs/utility/snippets.py | 6 +- tux/cogs/utility/tldr.py | 3 +- tux/handlers/activity.py | 3 +- tux/handlers/error.py | 3 +- tux/handlers/event.py | 3 +- tux/help.py | 35 +++++----- tux/utils/flags.py | 4 +- 17 files changed, 134 insertions(+), 106 deletions(-) diff --git a/tux/cogs/admin/mail.py b/tux/cogs/admin/mail.py index 112aa7e..7b4a302 100644 --- a/tux/cogs/admin/mail.py +++ b/tux/cogs/admin/mail.py @@ -99,7 +99,8 @@ class Mail(commands.Cog): delete_after=30, ) - def _generate_password(self) -> str: + @staticmethod + def _generate_password() -> str: password = "changeme" + "".join(str(random.randint(0, 9)) for _ in range(6)) password += "".join(random.choice("!@#$%^&*") for _ in range(4)) return password @@ -161,7 +162,8 @@ class Mail(commands.Cog): delete_after=30, ) - def _extract_mailbox_info(self, result: list[dict[str, str | None]]) -> str | None: + @staticmethod + def _extract_mailbox_info(result: list[dict[str, str | None]]) -> str | None: for item in result: if "msg" in item: msg = item["msg"] @@ -173,8 +175,8 @@ class Mail(commands.Cog): return None + @staticmethod async def _send_dm( - self, interaction: discord.Interaction, member: discord.Member, mailbox_info: str, diff --git a/tux/cogs/fun/imgeffect.py b/tux/cogs/fun/imgeffect.py index fe63538..c4dcb1e 100644 --- a/tux/cogs/fun/imgeffect.py +++ b/tux/cogs/fun/imgeffect.py @@ -14,71 +14,46 @@ from tux.ui.embeds import EmbedCreator class ImgEffect(commands.Cog): def __init__(self, bot: Tux) -> None: self.bot = bot - self.allowed_mimetypes = [ - "image/jpeg", - "image/png", - ] + self.allowed_mimetypes = ["image/jpeg", "image/png"] imgeffect = app_commands.Group(name="imgeffect", description="Image effects") - @imgeffect.command( - name="deepfry", - description="Deepfry an image", - ) + @imgeffect.command(name="deepfry", description="Deepfry an image") async def deepfry(self, interaction: discord.Interaction, image: discord.Attachment) -> None: - """ - Deepfry an image. - - Parameters - ---------- - interaction : discord.Interaction - The interaction object for the command. - image : discord.File - The image to deepfry. - """ - - # check if the image is a image - logger.info(f"Content type: {image.content_type}, Filename: {image.filename}, URL: {image.url}") - - if image.content_type not in self.allowed_mimetypes: - logger.error("The file is not a permitted image.") - - embed = EmbedCreator.create_embed( - bot=self.bot, - embed_type=EmbedCreator.ERROR, - user_name=interaction.user.name, - user_display_avatar=interaction.user.display_avatar.url, - title="Invalid File", - description="The file must be an image. Allowed types are PNG, JPEG, and JPG.", - ) - - await interaction.response.send_message(embed=embed, ephemeral=True) + if not self.is_valid_image(image): + await self.send_invalid_image_response(interaction) return - # say that the image is being processed - logger.info("Processing image...") await interaction.response.defer(ephemeral=True) - # open url with PIL - logger.info("Opening image with PIL and HTTPX...") + pil_image = await self.fetch_image(image.url) + + if pil_image: + deepfried_image = self.deepfry_image(pil_image) + await self.send_deepfried_image(interaction, deepfried_image) + + else: + await self.send_error_response(interaction) + + def is_valid_image(self, image: discord.Attachment) -> bool: + logger.info(f"Content type: {image.content_type}, Filename: {image.filename}, URL: {image.url}") + + return image.content_type in self.allowed_mimetypes + + @staticmethod + async def fetch_image(url: str) -> Image.Image: + logger.info("Fetching image from URL with HTTPX...") + async with httpx.AsyncClient() as client: - response = await client.get(image.url) + response = await client.get(url) - pil_image = Image.open(io.BytesIO(response.content)) - pil_image = pil_image.convert("RGB") - logger.info("Image opened with PIL.") + return Image.open(io.BytesIO(response.content)).convert("RGB") - # resize image to 25% then back to original size - logger.info("Resizing image...") + @staticmethod + def deepfry_image(pil_image: Image.Image) -> Image.Image: pil_image = pil_image.resize((int(pil_image.width * 0.25), int(pil_image.height * 0.25))) - logger.info("Image resized.") - - # increase sharpness - logger.info("Increasing sharpness...") pil_image = ImageEnhance.Sharpness(pil_image).enhance(100.0) - logger.info("Sharpness increased.") - logger.info("Adjusting color...") r = pil_image.split()[0] r = ImageEnhance.Contrast(r).enhance(2.0) r = ImageEnhance.Brightness(r).enhance(1.5) @@ -86,14 +61,43 @@ class ImgEffect(commands.Cog): colours = ((254, 0, 2), (255, 255, 15)) r = ImageOps.colorize(r, colours[0], colours[1]) pil_image = Image.blend(pil_image, r, 0.75) - logger.info("Color adjustment complete.") - # send image - logger.info("Sending image...") - pil_image = pil_image.resize((int(pil_image.width * 4), int(pil_image.height * 4))) + return pil_image.resize((int(pil_image.width * 4), int(pil_image.height * 4))) + + async def send_invalid_image_response(self, interaction: discord.Interaction) -> None: + logger.error("The file is not a permitted image.") + + embed = EmbedCreator.create_embed( + bot=self.bot, + embed_type=EmbedCreator.ERROR, + user_name=interaction.user.name, + user_display_avatar=interaction.user.display_avatar.url, + title="Invalid File", + description="The file must be an image. Allowed types are PNG, JPEG, and JPG.", + ) + + await interaction.response.send_message(embed=embed, ephemeral=True) + + async def send_error_response(self, interaction: discord.Interaction) -> None: + logger.error("Error processing the image.") + + embed = EmbedCreator.create_embed( + bot=self.bot, + embed_type=EmbedCreator.ERROR, + user_name=interaction.user.name, + user_display_avatar=interaction.user.display_avatar.url, + title="Error", + description="An error occurred while processing the image.", + ) + + await interaction.response.send_message(embed=embed, ephemeral=True) + + @staticmethod + async def send_deepfried_image(interaction: discord.Interaction, deepfried_image: Image.Image) -> None: arr = io.BytesIO() - pil_image.save(arr, format="JPEG", quality=1) + deepfried_image.save(arr, format="JPEG", quality=1) arr.seek(0) + file = discord.File(arr, filename="deepfried.jpg") await interaction.followup.send(file=file, ephemeral=True) diff --git a/tux/cogs/fun/random.py b/tux/cogs/fun/random.py index 33462c7..ab58ea9 100644 --- a/tux/cogs/fun/random.py +++ b/tux/cogs/fun/random.py @@ -57,7 +57,13 @@ class Random(commands.Cog): aliases=["eightball", "8b"], ) @commands.guild_only() - async def eight_ball(self, ctx: commands.Context[Tux], *, question: str, cow: bool = False) -> None: + async def eight_ball( + self, + ctx: commands.Context[Tux], + *, + question: str, + cow: bool = False, + ) -> None: """ Ask the magic 8ball a question. diff --git a/tux/cogs/info/info.py b/tux/cogs/info/info.py index 4f475b5..41df0af 100644 --- a/tux/cogs/info/info.py +++ b/tux/cogs/info/info.py @@ -65,7 +65,7 @@ class Info(commands.Cog): custom_color=discord.Color.blurple(), custom_author_text="Server Information", custom_author_icon_url=guild.icon.url, - custom_footer_text=f"ID: {guild.id} | Created: {guild.created_at.strftime('%B %d, %Y')}", + custom_footer_text=f"ID: {guild.id} | Created: {guild.created_at.strftime("%B %d, %Y")}", ) .add_field(name="Owner", value=str(guild.owner.mention) if guild.owner else "Unknown") .add_field(name="Vanity URL", value=guild.vanity_url_code or "None") @@ -73,7 +73,7 @@ class Info(commands.Cog): .add_field(name="Text Channels", value=len(guild.text_channels)) .add_field(name="Voice Channels", value=len(guild.voice_channels)) .add_field(name="Forum Channels", value=len(guild.forums)) - .add_field(name="Emojis", value=f"{len(guild.emojis)}/{2*guild.emoji_limit}") + .add_field(name="Emojis", value=f"{len(guild.emojis)}/{2 * guild.emoji_limit}") .add_field(name="Stickers", value=f"{len(guild.stickers)}/{guild.sticker_limit}") .add_field(name="Roles", value=len(guild.roles)) .add_field(name="Humans", value=sum(not member.bot for member in guild.members)) @@ -213,7 +213,7 @@ class Info(commands.Cog): menu: ViewMenu = ViewMenu(ctx, menu_type=ViewMenu.TypeEmbed) for chunk in chunks: page_embed: discord.Embed = embed.copy() - page_embed.description = f"{list_type.capitalize()} list for {guild_name}:\n{' '.join(chunk)}" + page_embed.description = f"{list_type.capitalize()} list for {guild_name}:\n{" ".join(chunk)}" menu.add_page(page_embed) buttons = [ @@ -229,7 +229,8 @@ class Info(commands.Cog): await menu.start() - def _chunks(self, it: Iterator[str], size: int) -> Generator[list[str], None, None]: + @staticmethod + def _chunks(it: Iterator[str], size: int) -> Generator[list[str], None, None]: """ Split an iterator into chunks of a specified size. diff --git a/tux/cogs/moderation/__init__.py b/tux/cogs/moderation/__init__.py index c2e1080..e0bc929 100644 --- a/tux/cogs/moderation/__init__.py +++ b/tux/cogs/moderation/__init__.py @@ -100,8 +100,8 @@ class ModerationCogBase(commands.Cog): if isinstance(log_channel, discord.TextChannel): await log_channel.send(embed=embed) + @staticmethod async def send_dm( - self, ctx: commands.Context[Tux], silent: bool, user: discord.Member, diff --git a/tux/cogs/moderation/cases.py b/tux/cogs/moderation/cases.py index a80e0b7..098b632 100644 --- a/tux/cogs/moderation/cases.py +++ b/tux/cogs/moderation/cases.py @@ -319,8 +319,8 @@ class Cases(ModerationCogBase): await menu.start() + @staticmethod def _create_case_fields( - self, moderator: discord.Member, user: discord.Member | discord.User, reason: str, @@ -361,7 +361,8 @@ class Cases(ModerationCogBase): return embed - def _format_emoji(self, emoji: discord.Emoji | None) -> str: + @staticmethod + def _format_emoji(emoji: discord.Emoji | None) -> str: return f"<:{emoji.name}:{emoji.id}>" if emoji else "" def _get_case_status_emoji(self, case_status: bool | None) -> discord.Emoji | None: @@ -392,16 +393,16 @@ class Cases(ModerationCogBase): def _get_case_action_emoji(self, case_type: CaseType) -> discord.Emoji | None: action = None - if case_type in [ + if case_type in { CaseType.BAN, CaseType.KICK, CaseType.TIMEOUT, CaseType.WARN, CaseType.JAIL, CaseType.SNIPPETBAN, - ]: + }: action = "added" - elif case_type in [CaseType.UNBAN, CaseType.UNTIMEOUT, CaseType.UNJAIL, CaseType.SNIPPETUNBAN]: + elif case_type in {CaseType.UNBAN, CaseType.UNTIMEOUT, CaseType.UNJAIL, CaseType.SNIPPETUNBAN}: action = "removed" if action is not None: @@ -410,8 +411,8 @@ class Cases(ModerationCogBase): return self.bot.get_emoji(emoji_id) return None + @staticmethod def _get_case_description( - self, case: Case, case_status_emoji: str, case_type_emoji: str, diff --git a/tux/cogs/moderation/jail.py b/tux/cogs/moderation/jail.py index 5a51bcb..9155ba3 100644 --- a/tux/cogs/moderation/jail.py +++ b/tux/cogs/moderation/jail.py @@ -105,8 +105,8 @@ class Jail(ModerationCogBase): dm_sent = await self.send_dm(ctx, flags.silent, member, flags.reason, "jailed") await self.handle_case_response(ctx, CaseType.JAIL, case.case_number, flags.reason, member, dm_sent) + @staticmethod def _get_manageable_roles( - self, member: discord.Member, jail_role: discord.Role, ) -> list[discord.Role]: diff --git a/tux/cogs/moderation/slowmode.py b/tux/cogs/moderation/slowmode.py index 0d4ec14..a55bd39 100644 --- a/tux/cogs/moderation/slowmode.py +++ b/tux/cogs/moderation/slowmode.py @@ -15,7 +15,8 @@ class Slowmode(commands.Cog): @commands.hybrid_command( name="slowmode", aliases=["sm"], - usage="slowmode [channel]\nor slowmode [channel] ", # only place where generate_usage shouldn't be used + # only place where generate_usage shouldn't be used: + usage="slowmode [channel]\nor slowmode [channel] ", ) @commands.guild_only() @checks.has_pl(2) @@ -83,11 +84,12 @@ class Slowmode(commands.Cog): return action, channel - def _get_channel(self, ctx: commands.Context[Tux]) -> discord.TextChannel | discord.Thread | None: + @staticmethod + def _get_channel(ctx: commands.Context[Tux]) -> discord.TextChannel | discord.Thread | None: return ctx.channel if isinstance(ctx.channel, discord.TextChannel | discord.Thread) else None + @staticmethod async def _get_slowmode( - self, ctx: commands.Context[Tux], channel: discord.TextChannel | discord.Thread, ) -> None: @@ -135,7 +137,8 @@ class Slowmode(commands.Cog): await ctx.send(f"Failed to set slowmode. Error: {error}", delete_after=30, ephemeral=True) logger.error(f"Failed to set slowmode. Error: {error}") - def _parse_delay(self, delay: str) -> int | None: + @staticmethod + def _parse_delay(delay: str) -> int | None: try: if delay.endswith("s"): delay = delay[:-1] diff --git a/tux/cogs/services/bookmarks.py b/tux/cogs/services/bookmarks.py index 7e86a8b..d1135be 100644 --- a/tux/cogs/services/bookmarks.py +++ b/tux/cogs/services/bookmarks.py @@ -52,7 +52,7 @@ class Bookmarks(commands.Cog): message: discord.Message, ) -> discord.Embed: if len(message.content) > CONST.EMBED_MAX_DESC_LENGTH: - message.content = f"{message.content[:CONST.EMBED_MAX_DESC_LENGTH - 3]}..." + message.content = f"{message.content[: CONST.EMBED_MAX_DESC_LENGTH - 3]}..." embed = EmbedCreator.create_embed( bot=self.bot, @@ -71,8 +71,8 @@ class Bookmarks(commands.Cog): return embed + @staticmethod async def _send_bookmark( - self, user: discord.User, message: discord.Message, embed: discord.Embed, diff --git a/tux/cogs/utility/run.py b/tux/cogs/utility/run.py index 72644ef..4fe8e59 100644 --- a/tux/cogs/utility/run.py +++ b/tux/cogs/utility/run.py @@ -46,7 +46,8 @@ class Run(commands.Cog): self.run.usage = generate_usage(self.run) self.languages.usage = generate_usage(self.languages) - def remove_ansi(self, ansi: str) -> str: + @staticmethod + def remove_ansi(ansi: str) -> str: """ Converts ANSI encoded text into non-ANSI. @@ -63,7 +64,8 @@ class Run(commands.Cog): return ansi_re.sub("", ansi) - def remove_backticks(self, st: str) -> str: + @staticmethod + def remove_backticks(st: str) -> str: """ Removes backticks from the provided string. @@ -277,7 +279,7 @@ class Run(commands.Cog): code, ) await msg.delete() - if filtered_output == "" and gen_one == "" and normalized_lang == "": + if not filtered_output and not gen_one and not normalized_lang: return await self.send_embedded_reply( ctx, @@ -340,7 +342,7 @@ class Run(commands.Cog): user_name=ctx.author.name, user_display_avatar=ctx.author.display_avatar.url, title="Supported Languages", - description=f"```{', '.join(compiler_map.keys())}```", + description=f"```{", ".join(compiler_map.keys())}```", ) await ctx.send(embed=embed) diff --git a/tux/cogs/utility/snippets.py b/tux/cogs/utility/snippets.py index dedfb96..8bc9c8d 100644 --- a/tux/cogs/utility/snippets.py +++ b/tux/cogs/utility/snippets.py @@ -152,7 +152,7 @@ class Snippets(commands.Cog): text = "```\n" for i, snippet in enumerate(snippets[:10]): - text += f"{i+1}. {snippet.snippet_name.ljust(20)} | uses: {snippet.uses}\n" + text += f"{i + 1}. {snippet.snippet_name.ljust(20)} | uses: {snippet.uses}\n" text += "```" # only show top 10, no pagination @@ -321,7 +321,7 @@ class Snippets(commands.Cog): embed.add_field(name="Name", value=snippet.snippet_name, inline=False) embed.add_field( name="Author", - value=f"{author.mention if author else f'<@!{snippet.snippet_user_id}>'}", + value=f"{author.mention if author else f"<@!{snippet.snippet_user_id}>"}", inline=False, ) embed.add_field(name="Content", value=f"> {snippet.snippet_content}", inline=False) @@ -495,7 +495,7 @@ class Snippets(commands.Cog): if author := self.bot.get_user(snippet.snippet_user_id): with contextlib.suppress(discord.Forbidden): await author.send( - f"""Your snippet `{snippet.snippet_name}` has been {'locked' if status.locked else 'unlocked'}. + f"""Your snippet `{snippet.snippet_name}` has been {"locked" if status.locked else "unlocked"}. **What does this mean?** If a snippet is locked, it cannot be edited by anyone other than moderators. This means that you can no longer edit this snippet. diff --git a/tux/cogs/utility/tldr.py b/tux/cogs/utility/tldr.py index b6c7ada..029fbb9 100644 --- a/tux/cogs/utility/tldr.py +++ b/tux/cogs/utility/tldr.py @@ -135,7 +135,8 @@ class Tldr(commands.Cog): return self._run_subprocess(["tldr", "--list"], "No TLDR pages found.").split("\n") - def _run_subprocess(self, command_list: list[str], default_response: str) -> str: + @staticmethod + def _run_subprocess(command_list: list[str], default_response: str) -> str: """ Helper method to run subprocesses for CLI interactions. diff --git a/tux/handlers/activity.py b/tux/handlers/activity.py index 8217e6d..683c0ed 100644 --- a/tux/handlers/activity.py +++ b/tux/handlers/activity.py @@ -13,7 +13,8 @@ class ActivityHandler(commands.Cog): self.delay = delay self.activities = self.build_activity_list() - def build_activity_list(self) -> list[discord.Activity | discord.Streaming]: + @staticmethod + def build_activity_list() -> list[discord.Activity | discord.Streaming]: activity_data = [ {"type": discord.ActivityType.watching, "name": "{member_count} members"}, {"type": discord.ActivityType.watching, "name": "All Things Linux"}, diff --git a/tux/handlers/error.py b/tux/handlers/error.py index d8132cb..5e42eb0 100644 --- a/tux/handlers/error.py +++ b/tux/handlers/error.py @@ -273,7 +273,8 @@ class ErrorHandler(commands.Cog): return error_map.get(type(error), self.error_message).format(error=error) - def log_error_traceback(self, error: Exception) -> None: + @staticmethod + def log_error_traceback(error: Exception) -> None: """ Log the error traceback. diff --git a/tux/handlers/event.py b/tux/handlers/event.py index 4f5a068..868618c 100644 --- a/tux/handlers/event.py +++ b/tux/handlers/event.py @@ -20,7 +20,8 @@ class EventHandler(commands.Cog): async def on_guild_remove(self, guild: discord.Guild) -> None: await self.db.guild.delete_guild_by_id(guild.id) - async def handle_harmful_message(self, message: discord.Message) -> None: + @staticmethod + async def handle_harmful_message(message: discord.Message) -> None: if message.author.bot: return diff --git a/tux/help.py b/tux/help.py index b854b39..bd162a3 100644 --- a/tux/help.py +++ b/tux/help.py @@ -38,7 +38,8 @@ class TuxHelp(commands.HelpCommand): return self.context.clean_prefix or CONST.DEFAULT_PREFIX - def _embed_base(self, title: str, description: str | None = None) -> discord.Embed: + @staticmethod + def _embed_base(title: str, description: str | None = None) -> discord.Embed: """ Creates a base embed with uniform styling. @@ -61,8 +62,8 @@ class TuxHelp(commands.HelpCommand): color=CONST.EMBED_COLORS["DEFAULT"], ) + @staticmethod def _add_command_field( - self, embed: discord.Embed, command: commands.Command[Any, Any, Any], prefix: str, @@ -84,11 +85,12 @@ class TuxHelp(commands.HelpCommand): embed.add_field( name=f"{prefix}{command.qualified_name} ({command_aliases})", - value=f"> {command.short_doc or 'No documentation summary.'}", + value=f"> {command.short_doc or "No documentation summary."}", inline=False, ) - def _get_flag_type(self, flag_annotation: Any) -> str: + @staticmethod + def _get_flag_type(flag_annotation: Any) -> str: """ Determines the type of a flag based on its annotation. @@ -111,7 +113,8 @@ class TuxHelp(commands.HelpCommand): case _: return str(flag_annotation) - def _format_flag_name(self, flag: commands.Flag) -> str: + @staticmethod + def _format_flag_name(flag: commands.Flag) -> str: """ Formats the flag name based on whether it is required. @@ -158,11 +161,11 @@ class TuxHelp(commands.HelpCommand): flag_str = self._format_flag_name(flag) if flag.aliases: - flag_str += f" ({', '.join(flag.aliases)})" + flag_str += f" ({", ".join(flag.aliases)})" # else: # flag_str += f" : {flag_type}" - flag_str += f"\n\t{flag.description or 'No description provided.'}" + flag_str += f"\n\t{flag.description or "No description provided."}" if flag.default is not discord.utils.MISSING: flag_str += f"\n\tDefault: {flag.default}" @@ -289,12 +292,13 @@ class TuxHelp(commands.HelpCommand): for command in mapping_commands: cmd_name_and_aliases = f"`{command.name}`" if command.aliases: - cmd_name_and_aliases += f" ({', '.join(f'`{alias}`' for alias in command.aliases)})" + cmd_name_and_aliases += f" ({", ".join(f"`{alias}`" for alias in command.aliases)})" command_categories[cog_group][command.name] = cmd_name_and_aliases return command_categories - def _get_cog_groups(self) -> list[str]: + @staticmethod + def _get_cog_groups() -> list[str]: """ Retrieves a list of cog groups from the 'cogs' folder. @@ -349,8 +353,8 @@ class TuxHelp(commands.HelpCommand): return select_options + @staticmethod def _add_navigation_and_selection( - self, menu: ViewMenu, select_options: dict[discord.SelectOption, list[Page]], ) -> None: @@ -368,7 +372,8 @@ class TuxHelp(commands.HelpCommand): menu.add_select(ViewSelect(title="Command Categories", options=select_options)) menu.add_button(ViewButton.end_session()) - def _extract_cog_group(self, cog: commands.Cog) -> str | None: + @staticmethod + def _extract_cog_group(cog: commands.Cog) -> str | None: """ Extracts the cog group from a cog's string representation. @@ -424,7 +429,7 @@ class TuxHelp(commands.HelpCommand): embed = self._embed_base( title=f"{prefix}{command.qualified_name}", - description=f"> {command.help or 'No documentation available.'}", + description=f"> {command.help or "No documentation available."}", ) await self._add_command_help_fields(embed, command) @@ -450,12 +455,12 @@ class TuxHelp(commands.HelpCommand): embed.add_field( name="Usage", - value=f"`{prefix}{command.usage or 'No usage.'}`", + value=f"`{prefix}{command.usage or "No usage."}`", inline=False, ) embed.add_field( name="Aliases", - value=(f"`{', '.join(command.aliases)}`" if command.aliases else "No aliases."), + value=(f"`{", ".join(command.aliases)}`" if command.aliases else "No aliases."), inline=False, ) @@ -471,7 +476,7 @@ class TuxHelp(commands.HelpCommand): prefix = await self._get_prefix() - embed = self._embed_base(f"{group.name}", f"> {group.help or 'No documentation available.'}") + embed = self._embed_base(f"{group.name}", f"> {group.help or "No documentation available."}") await self._add_command_help_fields(embed, group) for command in group.commands: diff --git a/tux/utils/flags.py b/tux/utils/flags.py index 176b616..43785b6 100644 --- a/tux/utils/flags.py +++ b/tux/utils/flags.py @@ -37,7 +37,7 @@ def generate_usage( flags: dict[str, commands.Flag] = flag_converter.get_flags() if flag_converter else {} for param_name, param in parameters.items(): - if param_name in ["ctx", "flags"]: + if param_name in {"ctx", "flags"}: continue is_required = param.default == inspect.Parameter.empty matching_string = get_matching_string(param_name) @@ -60,7 +60,7 @@ def generate_usage( usage += f" {flag}" if optional_flags: - usage += f" [{' | '.join(optional_flags)}]" + usage += f" [{" | ".join(optional_flags)}]" return usage From 58614ddadbb4e7e941f2a89241f937b86d199062 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 21 Sep 2024 19:16:26 +0000 Subject: [PATCH 16/38] fix(deps): update dependency ruff to v0.6.7 --- poetry.lock | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/poetry.lock b/poetry.lock index ca48a22..6f2b1c7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2043,29 +2043,29 @@ pyasn1 = ">=0.1.3" [[package]] name = "ruff" -version = "0.6.6" +version = "0.6.7" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.6.6-py3-none-linux_armv6l.whl", hash = "sha256:f5bc5398457484fc0374425b43b030e4668ed4d2da8ee7fdda0e926c9f11ccfb"}, - {file = "ruff-0.6.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:515a698254c9c47bb84335281a170213b3ee5eb47feebe903e1be10087a167ce"}, - {file = "ruff-0.6.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6bb1b4995775f1837ab70f26698dd73852bbb82e8f70b175d2713c0354fe9182"}, - {file = "ruff-0.6.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69c546f412dfae8bb9cc4f27f0e45cdd554e42fecbb34f03312b93368e1cd0a6"}, - {file = "ruff-0.6.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:59627e97364329e4eae7d86fa7980c10e2b129e2293d25c478ebcb861b3e3fd6"}, - {file = "ruff-0.6.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94c3f78c3d32190aafbb6bc5410c96cfed0a88aadb49c3f852bbc2aa9783a7d8"}, - {file = "ruff-0.6.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:704da526c1e137f38c8a067a4a975fe6834b9f8ba7dbc5fd7503d58148851b8f"}, - {file = "ruff-0.6.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:efeede5815a24104579a0f6320660536c5ffc1c91ae94f8c65659af915fb9de9"}, - {file = "ruff-0.6.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e368aef0cc02ca3593eae2fb8186b81c9c2b3f39acaaa1108eb6b4d04617e61f"}, - {file = "ruff-0.6.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2653fc3b2a9315bd809725c88dd2446550099728d077a04191febb5ea79a4f79"}, - {file = "ruff-0.6.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:bb858cd9ce2d062503337c5b9784d7b583bcf9d1a43c4df6ccb5eab774fbafcb"}, - {file = "ruff-0.6.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:488f8e15c01ea9afb8c0ba35d55bd951f484d0c1b7c5fd746ce3c47ccdedce68"}, - {file = "ruff-0.6.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:aefb0bd15f1cfa4c9c227b6120573bb3d6c4ee3b29fb54a5ad58f03859bc43c6"}, - {file = "ruff-0.6.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a4c0698cc780bcb2c61496cbd56b6a3ac0ad858c966652f7dbf4ceb029252fbe"}, - {file = "ruff-0.6.6-py3-none-win32.whl", hash = "sha256:aadf81ddc8ab5b62da7aae78a91ec933cbae9f8f1663ec0325dae2c364e4ad84"}, - {file = "ruff-0.6.6-py3-none-win_amd64.whl", hash = "sha256:0adb801771bc1f1b8cf4e0a6fdc30776e7c1894810ff3b344e50da82ef50eeb1"}, - {file = "ruff-0.6.6-py3-none-win_arm64.whl", hash = "sha256:4b4d32c137bc781c298964dd4e52f07d6f7d57c03eae97a72d97856844aa510a"}, - {file = "ruff-0.6.6.tar.gz", hash = "sha256:0fc030b6fd14814d69ac0196396f6761921bd20831725c7361e1b8100b818034"}, + {file = "ruff-0.6.7-py3-none-linux_armv6l.whl", hash = "sha256:08277b217534bfdcc2e1377f7f933e1c7957453e8a79764d004e44c40db923f2"}, + {file = "ruff-0.6.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:c6707a32e03b791f4448dc0dce24b636cbcdee4dd5607adc24e5ee73fd86c00a"}, + {file = "ruff-0.6.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:533d66b7774ef224e7cf91506a7dafcc9e8ec7c059263ec46629e54e7b1f90ab"}, + {file = "ruff-0.6.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17a86aac6f915932d259f7bec79173e356165518859f94649d8c50b81ff087e9"}, + {file = "ruff-0.6.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b3f8822defd260ae2460ea3832b24d37d203c3577f48b055590a426a722d50ef"}, + {file = "ruff-0.6.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9ba4efe5c6dbbb58be58dd83feedb83b5e95c00091bf09987b4baf510fee5c99"}, + {file = "ruff-0.6.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:525201b77f94d2b54868f0cbe5edc018e64c22563da6c5c2e5c107a4e85c1c0d"}, + {file = "ruff-0.6.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8854450839f339e1049fdbe15d875384242b8e85d5c6947bb2faad33c651020b"}, + {file = "ruff-0.6.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f0b62056246234d59cbf2ea66e84812dc9ec4540518e37553513392c171cb18"}, + {file = "ruff-0.6.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b1462fa56c832dc0cea5b4041cfc9c97813505d11cce74ebc6d1aae068de36b"}, + {file = "ruff-0.6.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:02b083770e4cdb1495ed313f5694c62808e71764ec6ee5db84eedd82fd32d8f5"}, + {file = "ruff-0.6.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:0c05fd37013de36dfa883a3854fae57b3113aaa8abf5dea79202675991d48624"}, + {file = "ruff-0.6.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f49c9caa28d9bbfac4a637ae10327b3db00f47d038f3fbb2195c4d682e925b14"}, + {file = "ruff-0.6.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a0e1655868164e114ba43a908fd2d64a271a23660195017c17691fb6355d59bb"}, + {file = "ruff-0.6.7-py3-none-win32.whl", hash = "sha256:a939ca435b49f6966a7dd64b765c9df16f1faed0ca3b6f16acdf7731969deb35"}, + {file = "ruff-0.6.7-py3-none-win_amd64.whl", hash = "sha256:590445eec5653f36248584579c06252ad2e110a5d1f32db5420de35fb0e1c977"}, + {file = "ruff-0.6.7-py3-none-win_arm64.whl", hash = "sha256:b28f0d5e2f771c1fe3c7a45d3f53916fc74a480698c4b5731f0bea61e52137c8"}, + {file = "ruff-0.6.7.tar.gz", hash = "sha256:44e52129d82266fa59b587e2cd74def5637b730a69c4542525dfdecfaae38bd5"}, ] [[package]] From 5d807114f05c887a3d913748665ab2d97959820f Mon Sep 17 00:00:00 2001 From: electron271 <66094410+electron271@users.noreply.github.com> Date: Mon, 23 Sep 2024 00:26:31 -0500 Subject: [PATCH 17/38] fix(fact.py) remove duplicate --- tux/cogs/fun/fact.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tux/cogs/fun/fact.py b/tux/cogs/fun/fact.py index 12b135c..f2fc1ad 100644 --- a/tux/cogs/fun/fact.py +++ b/tux/cogs/fun/fact.py @@ -17,7 +17,6 @@ class Fact(commands.Cog): "Linus Torvalds was around 22 years old when he started work on the Linux Kernel in 1991. In the same year, he also released prototypes of the kernel publicly.", "Linux's 1.0 release was in March 1994.", "Less than 1% of the latest kernel release includes code written by Linus Torvalds.", - "Linux is used by every major space programme in the world.", "Approximately 13.3% of the latest Linux kernel is made up of blank lines.", "Vim has various easter eggs. A notable one is found by typing :help 42 into the command bar.", "Slackware is the oldest active linux distribution being released on the 17th July 1993.", From 356176560fde9a692fd0f6e0d7b58b0fd85d8539 Mon Sep 17 00:00:00 2001 From: Kasen Engel <86841460+FluxC0@users.noreply.github.com> Date: Mon, 23 Sep 2024 16:45:12 +0000 Subject: [PATCH 18/38] add pollban command cog --- tux/cogs/moderation/pollban.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tux/cogs/moderation/pollban.py diff --git a/tux/cogs/moderation/pollban.py b/tux/cogs/moderation/pollban.py new file mode 100644 index 0000000..e69de29 From 06dfab430410a9f8ffe6c7bce949b39867948c03 Mon Sep 17 00:00:00 2001 From: Kasen Engel <86841460+FluxC0@users.noreply.github.com> Date: Mon, 23 Sep 2024 17:28:19 +0000 Subject: [PATCH 19/38] update flags.py to include pollban/unban --- tux/utils/flags.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tux/utils/flags.py b/tux/utils/flags.py index 43785b6..f876dc2 100644 --- a/tux/utils/flags.py +++ b/tux/utils/flags.py @@ -309,3 +309,33 @@ class SnippetUnbanFlags(commands.FlagConverter, case_insensitive=True, delimiter aliases=["s", "quiet"], default=False, ) + + +class PollBanFlags(commands.FlagConverter, case_insensitive=True, delimiter=" ", prefix="-"): + reason: str = commands.flag( + name="reason", + description="Reason for the poll ban.", + aliases=["r"], + default=MISSING, + ) + silent: bool = commands.flag( + name="silent", + description="Do not send a DM to the target.", + aliases=["s", "quiet"], + default=False, + ) + + +class PollUnbanFlags(commands.FlagConverter, case_insensitive=True, delimiter=" ", prefix="-"): + reason: str = commands.flag( + name="reason", + description="Reason for the poll unban", + aliases=["r"], + default=MISSING, + ) + silent: bool = commands.flag( + name="silent", + description="Do not send a DM to the target.", + aliases=["s", "quiet"], + default=False, + ) From 1b5b8c5f483f947090de317c2a98c3ed2a7d73d8 Mon Sep 17 00:00:00 2001 From: Kasen Engel <86841460+FluxC0@users.noreply.github.com> Date: Mon, 23 Sep 2024 17:31:11 +0000 Subject: [PATCH 20/38] update schema.prisma casetype --- prisma/schema.prisma | 2 ++ 1 file changed, 2 insertions(+) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index f5cd1b4..cc45cd9 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -170,4 +170,6 @@ enum CaseType { UNJAIL SNIPPETUNBAN UNTEMPBAN + POLLBAN + POLLUNBAN } From 65466b5b25c6dd98c3487a83140c01c32cbfbd0a Mon Sep 17 00:00:00 2001 From: Kasen Engel <86841460+FluxC0@users.noreply.github.com> Date: Mon, 23 Sep 2024 17:39:32 +0000 Subject: [PATCH 21/38] Added the code, heavily derived off of the snippet ban code. gonna integrate with polls next. --- tux/cogs/moderation/pollban.py | 96 ++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/tux/cogs/moderation/pollban.py b/tux/cogs/moderation/pollban.py index e69de29..ac14639 100644 --- a/tux/cogs/moderation/pollban.py +++ b/tux/cogs/moderation/pollban.py @@ -0,0 +1,96 @@ +import discord +from discord.ext import commands +from loguru import logger + +from prisma.enums import CaseType +from tux.bot import Tux +from tux.database.controllers.case import CaseController +from tux.utils import checks +from tux.utils.flags import PollBanFlags, generate_usage + +from . import ModerationCogBase + + +class PollBan(ModerationCogBase): + def __init__(self, bot: Tux) -> None: + super().__init__(bot) + self.case_controller = CaseController() + self.poll_ban.usage = generate_usage(self.poll_ban, PollBanFlags) + + @commands.hybrid_command( + name="pollban", + aliases=["pb"], + ) + @commands.guild_only() + @checks.has_pl(3) + async def poll_ban( + self, + ctx: commands.Context[Tux], + member: discord.Member, + *, + flags: PollBanFlags, + ) -> None: + """ + Ban a user from creating polls using tux.. + + Parameters + ---------- + ctx : commands.Context[Tux] + The context object. + member : discord.Member + The member to poll ban. + flags : PollBanFlags + The flags for the command. (reason: str, silent: bool) + """ + + assert ctx.guild + + if await self.is_pollbanned(ctx.guild.id, member.id): + await ctx.send("User is already poll banned.", delete_after=30, ephemeral=True) + return + + try: + case = await self.db.case.insert_case( + case_user_id=member.id, + case_moderator_id=ctx.author.id, + case_type=CaseType.POLLBAN, + case_reason=flags.reason, + guild_id=ctx.guild.id, + ) + + except Exception as e: + logger.error(f"Failed to ban {member}. {e}") + await ctx.send(f"Failed to ban {member}. {e}", delete_after=30) + return + + dm_sent = await self.send_dm(ctx, flags.silent, member, flags.reason, "poll banned") + await self.handle_case_response(ctx, CaseType.POLLBAN, case.case_number, flags.reason, member, dm_sent) + + async def is_pollbanned(self, guild_id: int, user_id: int) -> bool: + """ + Check if a user is poll banned. + + Parameters + ---------- + guild_id : int + The ID of the guild to check in. + user_id : int + The ID of the user to check. + + Returns + ------- + bool + True if the user is poll banned, False otherwise. + """ + + ban_cases = await self.case_controller.get_all_cases_by_type(guild_id, CaseType.POLLBAN) + unban_cases = await self.case_controller.get_all_cases_by_type(guild_id, CaseType.POLLUNBAN) + + ban_count = sum(case.case_user_id == user_id for case in ban_cases) + unban_count = sum(case.case_user_id == user_id for case in unban_cases) + + return ban_count > unban_count + + +async def setup(bot: Tux) -> None: + await bot.add_cog(PollBan(bot)) From 7fe7b85f186edcc5c2a61d0de2dd0b6b19b240d0 Mon Sep 17 00:00:00 2001 From: Kasen Engel <86841460+FluxC0@users.noreply.github.com> Date: Mon, 23 Sep 2024 17:58:27 +0000 Subject: [PATCH 22/38] integrate poll banning into poll code, gonna see if this code is working --- tux/cogs/utility/poll.py | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/tux/cogs/utility/poll.py b/tux/cogs/utility/poll.py index 119d53e..d26d7df 100644 --- a/tux/cogs/utility/poll.py +++ b/tux/cogs/utility/poll.py @@ -3,7 +3,9 @@ from discord import app_commands from discord.ext import commands from loguru import logger +from prisma.enums import CaseType from tux.bot import Tux +from tux.database.controllers import CaseController from tux.ui.embeds import EmbedCreator # TODO: Create option inputs for the poll command instead of using a comma separated string @@ -12,6 +14,16 @@ from tux.ui.embeds import EmbedCreator class Poll(commands.Cog): def __init__(self, bot: Tux) -> None: self.bot = bot + self.case_controller = CaseController() + + async def is_pollbanned(self, guild_id: int, user_id: int) -> bool: + ban_cases = await self.case_controller.get_all_cases_by_type(guild_id, CaseType.POLLBAN) + unban_cases = await self.case_controller.get_all_cases_by_type(guild_id, CaseType.POLLUNBAN) + + ban_count = sum(case.case_user_id == user_id for case in ban_cases) + unban_count = sum(case.case_user_id == user_id for case in unban_cases) + + return ban_count > unban_count @commands.Cog.listener() # listen for messages async def on_message(self, message: discord.Message) -> None: @@ -40,7 +52,8 @@ class Poll(commands.Cog): @commands.Cog.listener() async def on_reaction_add(self, reaction: discord.Reaction, user: discord.User) -> None: # Block any reactions that are not numbers for the poll - + # NOTE: Today we observed in amazing detail that the pure will of 1 too many dedicated discord members can make + # the bot freak out whenever trying to remove reactions. Is there a better solution for this? if reaction.message.embeds: embed = reaction.message.embeds[0] if ( @@ -52,7 +65,13 @@ class Poll(commands.Cog): @app_commands.command(name="poll", description="Creates a poll.") @app_commands.describe(title="Title of the poll", options="Poll options, comma separated") - async def poll(self, interaction: discord.Interaction, title: str, options: str) -> None: + async def poll( + self, + interaction: discord.Interaction, + title: str, + options: str, + ctx: commands.Context[Tux], + ) -> None: """ Create a poll with a title and options. @@ -65,6 +84,7 @@ class Poll(commands.Cog): options : str The options for the poll, separated by commas. """ + assert ctx.guild # Split the options by comma options_list = options.split(",") @@ -72,6 +92,9 @@ class Poll(commands.Cog): # Remove any leading or trailing whitespaces from the options options_list = [option.strip() for option in options_list] + if await self.is_pollbanned(ctx.guild.id, ctx.author.id): + await ctx.send("You are banned from making polls.") + return # Check if the options count is between 2-9 if len(options_list) < 2 or len(options_list) > 9: embed = EmbedCreator.create_embed( From ee39d7bfca3043200ab40e585137104b8d2e7ba9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 20:34:38 +0000 Subject: [PATCH 23/38] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.6.6 → v0.6.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.6...v0.6.7) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8ca57a0..755664d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,7 +12,7 @@ repos: - id: check-toml - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.6 + rev: v0.6.7 hooks: # Run the linter. - id: ruff From ce6515782b7818ba7e1a8cc37fb33881a831a39d Mon Sep 17 00:00:00 2001 From: Kasen Engel Date: Mon, 23 Sep 2024 16:25:01 -0500 Subject: [PATCH 24/38] Finish up Pollban code and fix some bugs --- tux/cogs/moderation/pollban.py | 2 +- tux/cogs/utility/poll.py | 23 +++++++++++++---------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/tux/cogs/moderation/pollban.py b/tux/cogs/moderation/pollban.py index ac14639..131c4d8 100644 --- a/tux/cogs/moderation/pollban.py +++ b/tux/cogs/moderation/pollban.py @@ -31,7 +31,7 @@ class PollBan(ModerationCogBase): flags: PollBanFlags, ) -> None: """ - Ban a user from creating polls using tux.. + Ban a user from creating polls using tux. Parameters ---------- diff --git a/tux/cogs/utility/poll.py b/tux/cogs/utility/poll.py index d26d7df..73e8ed0 100644 --- a/tux/cogs/utility/poll.py +++ b/tux/cogs/utility/poll.py @@ -65,13 +65,7 @@ class Poll(commands.Cog): @app_commands.command(name="poll", description="Creates a poll.") @app_commands.describe(title="Title of the poll", options="Poll options, comma separated") - async def poll( - self, - interaction: discord.Interaction, - title: str, - options: str, - ctx: commands.Context[Tux], - ) -> None: + async def poll(self, interaction: discord.Interaction, title: str, options: str) -> None: """ Create a poll with a title and options. @@ -83,8 +77,9 @@ class Poll(commands.Cog): The title of the poll. options : str The options for the poll, separated by commas. + + """ - assert ctx.guild # Split the options by comma options_list = options.split(",") @@ -92,8 +87,16 @@ class Poll(commands.Cog): # Remove any leading or trailing whitespaces from the options options_list = [option.strip() for option in options_list] - if await self.is_pollbanned(ctx.guild.id, ctx.author.id): - await ctx.send("You are banned from making polls.") + if await self.is_pollbanned(interaction.guild_id, interaction.user.id): + embed = EmbedCreator.create_embed( + bot=self.bot, + embed_type=EmbedCreator.ERROR, + user_name=interaction.user.name, + user_display_avatar=interaction.user.display_avatar.url, + title="Poll Banned", + description="you are banned from making polls. think about what you have done.", + ) + await interaction.response.send_message(embed=embed, ephemeral=True) return # Check if the options count is between 2-9 if len(options_list) < 2 or len(options_list) > 9: From 7e1bb4f934f801a88b8d7445bf117505189c2b4b Mon Sep 17 00:00:00 2001 From: Kasen Engel Date: Mon, 23 Sep 2024 16:38:19 -0500 Subject: [PATCH 25/38] make unbanning people possible. --- tux/cogs/moderation/pollunban.py | 96 ++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 tux/cogs/moderation/pollunban.py diff --git a/tux/cogs/moderation/pollunban.py b/tux/cogs/moderation/pollunban.py new file mode 100644 index 0000000..c486caf --- /dev/null +++ b/tux/cogs/moderation/pollunban.py @@ -0,0 +1,96 @@ +import discord +from discord.ext import commands +from loguru import logger + +from prisma.enums import CaseType +from tux.bot import Tux +from tux.database.controllers.case import CaseController +from tux.utils import checks +from tux.utils.flags import PollUnbanFlags, generate_usage + +from . import ModerationCogBase + + +class PollUnban(ModerationCogBase): + def __init__(self, bot: Tux) -> None: + super().__init__(bot) + self.case_controller = CaseController() + self.poll_unban.usage = generate_usage(self.poll_unban, PollUnbanFlags) + + @commands.hybrid_command( + name="pollunban", + aliases=["pub"], + ) + @commands.guild_only() + @checks.has_pl(3) + async def poll_unban( + self, + ctx: commands.Context[Tux], + member: discord.Member, + *, + flags: PollUnbanFlags, + ): + """ + Unban a user from creating snippets. + + Parameters + ---------- + ctx : commands.Context[Tux] + The context object. + member : discord.Member + The member to snippet unban. + flags : PollUnbanFlags + The flags for the command. (reason: str, silent: bool) + """ + + assert ctx.guild + + if not await self.is_pollbanned(ctx.guild.id, member.id): + await ctx.send("User is not poll banned.", delete_after=30, ephemeral=True) + return + + try: + case = await self.db.case.insert_case( + case_user_id=member.id, + case_moderator_id=ctx.author.id, + case_type=CaseType.POLLUNBAN, + case_reason=flags.reason, + guild_id=ctx.guild.id, + ) + + except Exception as e: + logger.error(f"Failed to poll unban {member}. {e}") + await ctx.send(f"Failed to poll unban {member}. {e}", delete_after=30, ephemeral=True) + return + + dm_sent = await self.send_dm(ctx, flags.silent, member, flags.reason, "poll unbanned") + await self.handle_case_response(ctx, CaseType.POLLUNBAN, case.case_number, flags.reason, member, dm_sent) + + async def is_pollbanned(self, guild_id: int, user_id: int) -> bool: + """ + Check if a user is poll banned. + + Parameters + ---------- + guild_id : int + The ID of the guild to check in. + user_id : int + The ID of the user to check. + + Returns + ------- + bool + True if the user is snippet banned, False otherwise. + """ + + ban_cases = await self.case_controller.get_all_cases_by_type(guild_id, CaseType.POLLBAN) + unban_cases = await self.case_controller.get_all_cases_by_type(guild_id, CaseType.POLLUNBAN) + + ban_count = sum(case.case_user_id == user_id for case in ban_cases) + unban_count = sum(case.case_user_id == user_id for case in unban_cases) + + return ban_count > unban_count + + +async def setup(bot: Tux) -> None: + await bot.add_cog(PollUnban(bot)) From 6022a1d33dff0a738dd51665e73a404b4afb883d Mon Sep 17 00:00:00 2001 From: Kasen Engel Date: Mon, 23 Sep 2024 16:43:17 -0500 Subject: [PATCH 26/38] add assertation to poll.py --- tux/cogs/utility/poll.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tux/cogs/utility/poll.py b/tux/cogs/utility/poll.py index 73e8ed0..d5f6a3f 100644 --- a/tux/cogs/utility/poll.py +++ b/tux/cogs/utility/poll.py @@ -80,7 +80,7 @@ class Poll(commands.Cog): """ - + assert interaction # Split the options by comma options_list = options.split(",") From 6241e2199fa60fda911bee71fee0ac810689b965 Mon Sep 17 00:00:00 2001 From: Kasen Engel Date: Mon, 23 Sep 2024 16:46:18 -0500 Subject: [PATCH 27/38] add assertation (another) to poll.py --- tux/cogs/utility/poll.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tux/cogs/utility/poll.py b/tux/cogs/utility/poll.py index d5f6a3f..836a26d 100644 --- a/tux/cogs/utility/poll.py +++ b/tux/cogs/utility/poll.py @@ -80,7 +80,9 @@ class Poll(commands.Cog): """ - assert interaction + assert interaction.guild_id is int + assert interaction.user.id is int + # Split the options by comma options_list = options.split(",") From f1cfa59c992c75e8ae134622315d0314e4af84a8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 25 Sep 2024 09:08:05 +0000 Subject: [PATCH 28/38] chore(deps): update dependency mkdocs-material to v9.5.37 --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index ca48a22..77e904e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1160,13 +1160,13 @@ pyyaml = ">=5.1" [[package]] name = "mkdocs-material" -version = "9.5.35" +version = "9.5.37" description = "Documentation that simply works" optional = false python-versions = ">=3.8" files = [ - {file = "mkdocs_material-9.5.35-py3-none-any.whl", hash = "sha256:44e069d87732d29f4a2533ae0748fa0e67e270043270c71f04d0fba11a357b24"}, - {file = "mkdocs_material-9.5.35.tar.gz", hash = "sha256:0d233d7db067ac896bf22ee7950eebf2b1eaf26c155bb27382bf4174021cc117"}, + {file = "mkdocs_material-9.5.37-py3-none-any.whl", hash = "sha256:6e8a986abad77be5edec3dd77cf1ddf2480963fb297a8e971f87a82fd464b070"}, + {file = "mkdocs_material-9.5.37.tar.gz", hash = "sha256:2c31607431ec234db124031255b0a9d4f3e1c3ecc2c47ad97ecfff0460471941"}, ] [package.dependencies] From 573efb860d3bb3a0f2fcb803de1b0b94ac39a00a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 25 Sep 2024 09:08:18 +0000 Subject: [PATCH 29/38] fix(deps): update dependency pyright to v1.1.382 --- poetry.lock | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index ca48a22..041d72b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1758,21 +1758,23 @@ tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] [[package]] name = "pyright" -version = "1.1.381" +version = "1.1.382" description = "Command line wrapper for pyright" optional = false python-versions = ">=3.7" files = [ - {file = "pyright-1.1.381-py3-none-any.whl", hash = "sha256:5dc0aa80a265675d36abab59c674ae01dbe476714f91845b61b841d34aa99081"}, - {file = "pyright-1.1.381.tar.gz", hash = "sha256:314cf0c1351c189524fb10c7ac20688ecd470e8cc505c394d642c9c80bf7c3a5"}, + {file = "pyright-1.1.382-py3-none-any.whl", hash = "sha256:b6658802b2eca1107a6d63b6816903da4c7a53dffebc27cdbc02ebd904b7a18e"}, + {file = "pyright-1.1.382.tar.gz", hash = "sha256:0c953837aa9f1e1d8d46772ee7ebae845104db657e9216834dbdde567a11f177"}, ] [package.dependencies] nodeenv = ">=1.6.0" +typing-extensions = ">=4.1" [package.extras] -all = ["twine (>=3.4.1)"] +all = ["nodejs-wheel-binaries", "twine (>=3.4.1)"] dev = ["twine (>=3.4.1)"] +nodejs = ["nodejs-wheel-binaries"] [[package]] name = "python-dateutil" From a17d10bd13989fd8f5cf40f3d75090b344541bdd Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 26 Sep 2024 10:08:43 +0000 Subject: [PATCH 30/38] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tux/cogs/services/gif_limiter.py | 34 +++++++++++++++++++------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/tux/cogs/services/gif_limiter.py b/tux/cogs/services/gif_limiter.py index 922685f..3eda88d 100644 --- a/tux/cogs/services/gif_limiter.py +++ b/tux/cogs/services/gif_limiter.py @@ -15,6 +15,7 @@ class GifLimiter(commands.Cog): It keeps a list of GIF send times and routinely removes old times. It will prevent people from posting GIFs if the quotas are exceeded. """ + def __init__(self, bot: Tux) -> None: self.bot = bot @@ -42,29 +43,31 @@ class GifLimiter(commands.Cog): self.old_gif_remover.start() async def _should_process_message(self, message: discord.Message) -> bool: - """ Checks if a message contains a GIF and was not sent in a blacklisted channel """ - return not (len(message.embeds) == 0 - or "gif" not in message.content.lower() - or message.channel.id in self.gif_limit_exclude) + """Checks if a message contains a GIF and was not sent in a blacklisted channel""" + return not ( + len(message.embeds) == 0 + or "gif" not in message.content.lower() + or message.channel.id in self.gif_limit_exclude + ) async def _handle_gif_message(self, message: discord.Message) -> None: - """ Checks for ratelimit infringements """ + """Checks for ratelimit infringements""" async with self.gif_lock: channel: int = message.channel.id user: int = message.author.id if ( - channel in self.channelwide_gif_limits - and channel in self.recent_gifs_by_channel - and len(self.recent_gifs_by_channel[channel]) >= self.channelwide_gif_limits[channel] + channel in self.channelwide_gif_limits + and channel in self.recent_gifs_by_channel + and len(self.recent_gifs_by_channel[channel]) >= self.channelwide_gif_limits[channel] ): await self._delete_message(message, "for channel") return if ( - user in self.recent_gifs_by_user - and channel in self.user_gif_limits - and len(self.recent_gifs_by_user[user]) >= self.user_gif_limits[channel] + user in self.recent_gifs_by_user + and channel in self.user_gif_limits + and len(self.recent_gifs_by_user[user]) >= self.user_gif_limits[channel] ): await self._delete_message(message, "for user") return @@ -84,8 +87,8 @@ class GifLimiter(commands.Cog): @commands.Cog.listener() async def on_message(self, message: discord.Message) -> None: """Checks for GIFs in every sent message""" - - if (await self._should_process_message(message)): + + if await self._should_process_message(message): await self._handle_gif_message(message) @tasks.loop(seconds=20) @@ -95,7 +98,9 @@ class GifLimiter(commands.Cog): async with self.gif_lock: for channel_id, timestamps in self.recent_gifs_by_channel.items(): - self.recent_gifs_by_channel[channel_id] = [t for t in timestamps if current_time - t < self.recent_gif_age] + self.recent_gifs_by_channel[channel_id] = [ + t for t in timestamps if current_time - t < self.recent_gif_age + ] for user_id, timestamps in self.recent_gifs_by_user.items(): self.recent_gifs_by_user[user_id] = [t for t in timestamps if current_time - t < self.recent_gif_age] @@ -104,5 +109,6 @@ class GifLimiter(commands.Cog): if len(self.recent_gifs_by_user[user_id]) == 0: del self.recent_gifs_by_user[user_id] + async def setup(bot: Tux) -> None: await bot.add_cog(GifLimiter(bot)) From 66da128c892b5b7758ae0ea29af8697100900970 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 26 Sep 2024 10:50:22 +0000 Subject: [PATCH 31/38] chore(deps): update dependency mkdocs-material to v9.5.38 --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index e17981d..1fdc5bc 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1160,13 +1160,13 @@ pyyaml = ">=5.1" [[package]] name = "mkdocs-material" -version = "9.5.37" +version = "9.5.38" description = "Documentation that simply works" optional = false python-versions = ">=3.8" files = [ - {file = "mkdocs_material-9.5.37-py3-none-any.whl", hash = "sha256:6e8a986abad77be5edec3dd77cf1ddf2480963fb297a8e971f87a82fd464b070"}, - {file = "mkdocs_material-9.5.37.tar.gz", hash = "sha256:2c31607431ec234db124031255b0a9d4f3e1c3ecc2c47ad97ecfff0460471941"}, + {file = "mkdocs_material-9.5.38-py3-none-any.whl", hash = "sha256:d4779051d52ba9f1e7e344b34de95449c7c366c212b388e4a2db9a3db043c228"}, + {file = "mkdocs_material-9.5.38.tar.gz", hash = "sha256:1843c5171ad6b489550aeaf7358e5b7128cc03ddcf0fb4d91d19aa1e691a63b8"}, ] [package.dependencies] From 93694f29213f02d677d55274def44902bf03caf1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 26 Sep 2024 14:27:43 +0000 Subject: [PATCH 32/38] fix(deps): update dependency ruff to v0.6.8 --- poetry.lock | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/poetry.lock b/poetry.lock index e17981d..174b0fa 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2045,29 +2045,29 @@ pyasn1 = ">=0.1.3" [[package]] name = "ruff" -version = "0.6.7" +version = "0.6.8" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.6.7-py3-none-linux_armv6l.whl", hash = "sha256:08277b217534bfdcc2e1377f7f933e1c7957453e8a79764d004e44c40db923f2"}, - {file = "ruff-0.6.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:c6707a32e03b791f4448dc0dce24b636cbcdee4dd5607adc24e5ee73fd86c00a"}, - {file = "ruff-0.6.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:533d66b7774ef224e7cf91506a7dafcc9e8ec7c059263ec46629e54e7b1f90ab"}, - {file = "ruff-0.6.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17a86aac6f915932d259f7bec79173e356165518859f94649d8c50b81ff087e9"}, - {file = "ruff-0.6.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b3f8822defd260ae2460ea3832b24d37d203c3577f48b055590a426a722d50ef"}, - {file = "ruff-0.6.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9ba4efe5c6dbbb58be58dd83feedb83b5e95c00091bf09987b4baf510fee5c99"}, - {file = "ruff-0.6.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:525201b77f94d2b54868f0cbe5edc018e64c22563da6c5c2e5c107a4e85c1c0d"}, - {file = "ruff-0.6.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8854450839f339e1049fdbe15d875384242b8e85d5c6947bb2faad33c651020b"}, - {file = "ruff-0.6.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f0b62056246234d59cbf2ea66e84812dc9ec4540518e37553513392c171cb18"}, - {file = "ruff-0.6.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b1462fa56c832dc0cea5b4041cfc9c97813505d11cce74ebc6d1aae068de36b"}, - {file = "ruff-0.6.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:02b083770e4cdb1495ed313f5694c62808e71764ec6ee5db84eedd82fd32d8f5"}, - {file = "ruff-0.6.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:0c05fd37013de36dfa883a3854fae57b3113aaa8abf5dea79202675991d48624"}, - {file = "ruff-0.6.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f49c9caa28d9bbfac4a637ae10327b3db00f47d038f3fbb2195c4d682e925b14"}, - {file = "ruff-0.6.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a0e1655868164e114ba43a908fd2d64a271a23660195017c17691fb6355d59bb"}, - {file = "ruff-0.6.7-py3-none-win32.whl", hash = "sha256:a939ca435b49f6966a7dd64b765c9df16f1faed0ca3b6f16acdf7731969deb35"}, - {file = "ruff-0.6.7-py3-none-win_amd64.whl", hash = "sha256:590445eec5653f36248584579c06252ad2e110a5d1f32db5420de35fb0e1c977"}, - {file = "ruff-0.6.7-py3-none-win_arm64.whl", hash = "sha256:b28f0d5e2f771c1fe3c7a45d3f53916fc74a480698c4b5731f0bea61e52137c8"}, - {file = "ruff-0.6.7.tar.gz", hash = "sha256:44e52129d82266fa59b587e2cd74def5637b730a69c4542525dfdecfaae38bd5"}, + {file = "ruff-0.6.8-py3-none-linux_armv6l.whl", hash = "sha256:77944bca110ff0a43b768f05a529fecd0706aac7bcce36d7f1eeb4cbfca5f0f2"}, + {file = "ruff-0.6.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:27b87e1801e786cd6ede4ada3faa5e254ce774de835e6723fd94551464c56b8c"}, + {file = "ruff-0.6.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:cd48f945da2a6334f1793d7f701725a76ba93bf3d73c36f6b21fb04d5338dcf5"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:677e03c00f37c66cea033274295a983c7c546edea5043d0c798833adf4cf4c6f"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9f1476236b3eacfacfc0f66aa9e6cd39f2a624cb73ea99189556015f27c0bdeb"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f5a2f17c7d32991169195d52a04c95b256378bbf0de8cb98478351eb70d526f"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5fd0d4b7b1457c49e435ee1e437900ced9b35cb8dc5178921dfb7d98d65a08d0"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8034b19b993e9601f2ddf2c517451e17a6ab5cdb1c13fdff50c1442a7171d87"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6cfb227b932ba8ef6e56c9f875d987973cd5e35bc5d05f5abf045af78ad8e098"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ef0411eccfc3909269fed47c61ffebdcb84a04504bafa6b6df9b85c27e813b0"}, + {file = "ruff-0.6.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:007dee844738c3d2e6c24ab5bc7d43c99ba3e1943bd2d95d598582e9c1b27750"}, + {file = "ruff-0.6.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ce60058d3cdd8490e5e5471ef086b3f1e90ab872b548814e35930e21d848c9ce"}, + {file = "ruff-0.6.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1085c455d1b3fdb8021ad534379c60353b81ba079712bce7a900e834859182fa"}, + {file = "ruff-0.6.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:70edf6a93b19481affd287d696d9e311388d808671bc209fb8907b46a8c3af44"}, + {file = "ruff-0.6.8-py3-none-win32.whl", hash = "sha256:792213f7be25316f9b46b854df80a77e0da87ec66691e8f012f887b4a671ab5a"}, + {file = "ruff-0.6.8-py3-none-win_amd64.whl", hash = "sha256:ec0517dc0f37cad14a5319ba7bba6e7e339d03fbf967a6d69b0907d61be7a263"}, + {file = "ruff-0.6.8-py3-none-win_arm64.whl", hash = "sha256:8d3bb2e3fbb9875172119021a13eed38849e762499e3cfde9588e4b4d70968dc"}, + {file = "ruff-0.6.8.tar.gz", hash = "sha256:a5bf44b1aa0adaf6d9d20f86162b34f7c593bfedabc51239953e446aefc8ce18"}, ] [[package]] From 4205b0ccc1fda62b837882263e95f9638e07becd Mon Sep 17 00:00:00 2001 From: electron271 <66094410+electron271@users.noreply.github.com> Date: Thu, 26 Sep 2024 11:50:26 -0500 Subject: [PATCH 33/38] fix(settings.yml.example) small consistency fix --- config/settings.yml.example | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/settings.yml.example b/config/settings.yml.example index 8cfa314..5437cb0 100644 --- a/config/settings.yml.example +++ b/config/settings.yml.example @@ -10,8 +10,8 @@ USER_IDS: - 123456789012345679 BOT_OWNER: 123456789012345679 -TEMPVC_CATEGORY_ID: 123456789012345 -TEMPVC_CHANNEL_ID: 123456789012345 +TEMPVC_CATEGORY_ID: 123456789012345679 +TEMPVC_CHANNEL_ID: 123456789012345679 EMBED_COLORS: DEFAULT: 16044058 From b24c6bec3c93120aec2852f4d114dd9306b5c45b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 26 Sep 2024 19:09:18 +0000 Subject: [PATCH 34/38] chore(deps): update dependency ubuntu to v24 --- .github/workflows/linting.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index ed8f52f..f5e5bfa 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -9,7 +9,7 @@ permissions: jobs: Linting: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: 'Checkout Repository' uses: actions/checkout@v4 From 5097f5594e9252b908159e4d5e2eaf93db8e9523 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 27 Sep 2024 00:27:03 +0000 Subject: [PATCH 35/38] fix(deps): update dependency aiocache to v0.12.3 --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index e17981d..1400291 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2,13 +2,13 @@ [[package]] name = "aiocache" -version = "0.12.2" +version = "0.12.3" description = "multi backend asyncio cache" optional = false python-versions = "*" files = [ - {file = "aiocache-0.12.2-py2.py3-none-any.whl", hash = "sha256:9b6fa30634ab0bfc3ecc44928a91ff07c6ea16d27d55469636b296ebc6eb5918"}, - {file = "aiocache-0.12.2.tar.gz", hash = "sha256:b41c9a145b050a5dcbae1599f847db6dd445193b1f3bd172d8e0fe0cb9e96684"}, + {file = "aiocache-0.12.3-py2.py3-none-any.whl", hash = "sha256:889086fc24710f431937b87ad3720a289f7fc31c4fd8b68e9f918b9bacd8270d"}, + {file = "aiocache-0.12.3.tar.gz", hash = "sha256:f528b27bf4d436b497a1d0d1a8f59a542c153ab1e37c3621713cb376d44c4713"}, ] [package.extras] From 887ad4710f7aa0ed34ec3e60d3be22e472f2b6e4 Mon Sep 17 00:00:00 2001 From: electron271 <66094410+electron271@users.noreply.github.com> Date: Thu, 26 Sep 2024 20:33:23 -0500 Subject: [PATCH 36/38] fix(poll.py) update poll banned message for consistency and professionalism --- tux/cogs/utility/poll.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tux/cogs/utility/poll.py b/tux/cogs/utility/poll.py index 836a26d..1e40d1e 100644 --- a/tux/cogs/utility/poll.py +++ b/tux/cogs/utility/poll.py @@ -96,7 +96,7 @@ class Poll(commands.Cog): user_name=interaction.user.name, user_display_avatar=interaction.user.display_avatar.url, title="Poll Banned", - description="you are banned from making polls. think about what you have done.", + description="You are poll banned and cannot create a poll.", ) await interaction.response.send_message(embed=embed, ephemeral=True) return From 83b028b5b8a386430db58603dd8107c59506df0c Mon Sep 17 00:00:00 2001 From: electron271 <66094410+electron271@users.noreply.github.com> Date: Thu, 26 Sep 2024 20:37:00 -0500 Subject: [PATCH 37/38] note(poll.py) address note (see commit desc) this is just part of discord rate limiting, at the end of the day the reactions are still removed --- tux/cogs/utility/poll.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tux/cogs/utility/poll.py b/tux/cogs/utility/poll.py index 1e40d1e..67436b2 100644 --- a/tux/cogs/utility/poll.py +++ b/tux/cogs/utility/poll.py @@ -52,8 +52,6 @@ class Poll(commands.Cog): @commands.Cog.listener() async def on_reaction_add(self, reaction: discord.Reaction, user: discord.User) -> None: # Block any reactions that are not numbers for the poll - # NOTE: Today we observed in amazing detail that the pure will of 1 too many dedicated discord members can make - # the bot freak out whenever trying to remove reactions. Is there a better solution for this? if reaction.message.embeds: embed = reaction.message.embeds[0] if ( From afb937783d62c794abdd078174351a9d62a3091f Mon Sep 17 00:00:00 2001 From: electron271 <66094410+electron271@users.noreply.github.com> Date: Thu, 26 Sep 2024 20:59:50 -0500 Subject: [PATCH 38/38] finish pollban --- tux/cogs/moderation/__init__.py | 28 ++++++++++++++++++++++++++++ tux/cogs/moderation/pollban.py | 25 ------------------------- tux/cogs/moderation/pollunban.py | 25 ------------------------- tux/cogs/utility/poll.py | 26 +++++++++++++++++++++++--- 4 files changed, 51 insertions(+), 53 deletions(-) diff --git a/tux/cogs/moderation/__init__.py b/tux/cogs/moderation/__init__.py index e0bc929..04d61de 100644 --- a/tux/cogs/moderation/__init__.py +++ b/tux/cogs/moderation/__init__.py @@ -250,3 +250,31 @@ class ModerationCogBase(commands.Cog): await self.send_embed(ctx, embed, log_type="mod") await ctx.send(embed=embed, delete_after=30, ephemeral=True) + + async def is_pollbanned(self, guild_id: int, user_id: int) -> bool: + """ + Check if a user is poll banned. + + Parameters + ---------- + guild_id : int + The ID of the guild to check in. + user_id : int + The ID of the user to check. + + Returns + ------- + bool + True if the user is poll banned, False otherwise. + """ + + # ban_cases = await self.case_controller.get_all_cases_by_type(guild_id, CaseType.POLLBAN) + # unban_cases = await self.case_controller.get_all_cases_by_type(guild_id, CaseType.POLLUNBAN) + + ban_cases = await self.db.case.get_all_cases_by_type(guild_id, CaseType.POLLBAN) + unban_cases = await self.db.case.get_all_cases_by_type(guild_id, CaseType.POLLUNBAN) + + ban_count = sum(case.case_user_id == user_id for case in ban_cases) + unban_count = sum(case.case_user_id == user_id for case in unban_cases) + + return ban_count > unban_count diff --git a/tux/cogs/moderation/pollban.py b/tux/cogs/moderation/pollban.py index 131c4d8..6afbabd 100644 --- a/tux/cogs/moderation/pollban.py +++ b/tux/cogs/moderation/pollban.py @@ -66,31 +66,6 @@ class PollBan(ModerationCogBase): dm_sent = await self.send_dm(ctx, flags.silent, member, flags.reason, "poll banned") await self.handle_case_response(ctx, CaseType.POLLBAN, case.case_number, flags.reason, member, dm_sent) - async def is_pollbanned(self, guild_id: int, user_id: int) -> bool: - """ - Check if a user is poll banned. - - Parameters - ---------- - guild_id : int - The ID of the guild to check in. - user_id : int - The ID of the user to check. - - Returns - ------- - bool - True if the user is poll banned, False otherwise. - """ - - ban_cases = await self.case_controller.get_all_cases_by_type(guild_id, CaseType.POLLBAN) - unban_cases = await self.case_controller.get_all_cases_by_type(guild_id, CaseType.POLLUNBAN) - - ban_count = sum(case.case_user_id == user_id for case in ban_cases) - unban_count = sum(case.case_user_id == user_id for case in unban_cases) - - return ban_count > unban_count - async def setup(bot: Tux) -> None: await bot.add_cog(PollBan(bot)) diff --git a/tux/cogs/moderation/pollunban.py b/tux/cogs/moderation/pollunban.py index c486caf..dcf3ff2 100644 --- a/tux/cogs/moderation/pollunban.py +++ b/tux/cogs/moderation/pollunban.py @@ -66,31 +66,6 @@ class PollUnban(ModerationCogBase): dm_sent = await self.send_dm(ctx, flags.silent, member, flags.reason, "poll unbanned") await self.handle_case_response(ctx, CaseType.POLLUNBAN, case.case_number, flags.reason, member, dm_sent) - async def is_pollbanned(self, guild_id: int, user_id: int) -> bool: - """ - Check if a user is poll banned. - - Parameters - ---------- - guild_id : int - The ID of the guild to check in. - user_id : int - The ID of the user to check. - - Returns - ------- - bool - True if the user is snippet banned, False otherwise. - """ - - ban_cases = await self.case_controller.get_all_cases_by_type(guild_id, CaseType.POLLBAN) - unban_cases = await self.case_controller.get_all_cases_by_type(guild_id, CaseType.POLLUNBAN) - - ban_count = sum(case.case_user_id == user_id for case in ban_cases) - unban_count = sum(case.case_user_id == user_id for case in unban_cases) - - return ban_count > unban_count - async def setup(bot: Tux) -> None: await bot.add_cog(PollUnban(bot)) diff --git a/tux/cogs/utility/poll.py b/tux/cogs/utility/poll.py index 67436b2..513b006 100644 --- a/tux/cogs/utility/poll.py +++ b/tux/cogs/utility/poll.py @@ -16,14 +16,33 @@ class Poll(commands.Cog): self.bot = bot self.case_controller = CaseController() + # TODO: for the moment this is duplicated code from ModerationCogBase in a attempt to get the code out sooner async def is_pollbanned(self, guild_id: int, user_id: int) -> bool: + """ + Check if a user is poll banned. + + Parameters + ---------- + guild_id : int + The ID of the guild to check in. + user_id : int + The ID of the user to check. + + Returns + ------- + bool + True if the user is poll banned, False otherwise. + """ + ban_cases = await self.case_controller.get_all_cases_by_type(guild_id, CaseType.POLLBAN) unban_cases = await self.case_controller.get_all_cases_by_type(guild_id, CaseType.POLLUNBAN) ban_count = sum(case.case_user_id == user_id for case in ban_cases) unban_count = sum(case.case_user_id == user_id for case in unban_cases) - return ban_count > unban_count + return ( + ban_count > unban_count + ) # TODO: this implementation is flawed, if someone bans and unbans the same user multiple times, this will not work as expected @commands.Cog.listener() # listen for messages async def on_message(self, message: discord.Message) -> None: @@ -78,8 +97,9 @@ class Poll(commands.Cog): """ - assert interaction.guild_id is int - assert interaction.user.id is int + if interaction.guild_id is None: + await interaction.response.send_message("This command can only be used in a server.", ephemeral=True) + return # Split the options by comma options_list = options.split(",")