diff --git a/bot/config.py b/bot/config.py index 3710a6c0..a04e5176 100644 --- a/bot/config.py +++ b/bot/config.py @@ -41,6 +41,7 @@ class CoC(BaseModel): channel_id: int message_id: int role_id: int + session_role_id: int session_cookie: str diff --git a/bot/extensions/clashofcode/commands.py b/bot/extensions/clashofcode/commands.py index 7d44499c..8c24263b 100644 --- a/bot/extensions/clashofcode/commands.py +++ b/bot/extensions/clashofcode/commands.py @@ -4,23 +4,30 @@ from aiohttp import ContentTypeError from codingame.http import HTTPError from discord import app_commands +from discord.app_commands import Cooldown from discord.ext import commands from bot import core from bot.config import settings from bot.extensions.clashofcode.utils import coc_client, coc_helper -from bot.extensions.clashofcode.views import CreateCocView +from bot.extensions.clashofcode.views import CocMessageView, CreateCocView log = logging.getLogger(__name__) +def new_coc_cooldown(interaction: discord.Interaction) -> Cooldown | None: + """Dynamic cooldown for the /coc new command.""" + if settings.moderation.staff_role_id in [role.id for role in interaction.user.roles]: + return None + return Cooldown(1, 60 * 15) + + @app_commands.default_permissions(administrator=True) class ClashOfCode(commands.GroupCog, group_name="coc"): def __init__(self, bot: core.DiscordBot): self.bot = bot - self._create_coc_view = CreateCocView(timeout=None) - self.bot.add_view(self._create_coc_view) + self.bot.add_view(CocMessageView()) async def cog_load(self): await coc_client.login(remember_me_cookie=settings.coc.session_cookie) @@ -33,56 +40,217 @@ async def interaction_check(self, interaction: core.InteractionType): return False return True - @app_commands.command() + coc_session = app_commands.Group(name="session", description="Session commands") + + @coc_session.command() + @app_commands.checks.dynamic_cooldown( + new_coc_cooldown, key=lambda i: settings.moderation.staff_role_id in [role.id for role in i.user.roles] + ) async def new(self, interaction: core.InteractionType): - """Create a new coc for the session""" + """Creates a new Clash of Code session.""" + if coc_helper.session: + return await interaction.response.send_message("A session is already active.", ephemeral=True) + + await interaction.response.defer() + + coc_helper.session = True + coc_helper.host = interaction.user + + role = interaction.guild.get_role(settings.coc.session_role_id) + if not role: + coc_helper.session = False + coc_helper.host = None + return await interaction.followup.send("The session role could not be found.", ephemeral=True) + + try: + await interaction.user.add_roles(role) + except discord.HTTPException as e: + log.error(f"Failed to add role to {interaction.user.display_name}", exc_info=e) + + ping_role = interaction.guild.get_role(settings.coc.role_id) + + view = CocMessageView() + msg = await interaction.followup.send( + ( + f"**Hey, {ping_role.mention}, {interaction.user.mention} is hosting a Clash Of Code session!**\n" + f"Everyone else can join the session to get pinged!" + ), + allowed_mentions=discord.AllowedMentions(roles=True, users=True), + view=view, + ) + try: + await msg.pin() + except discord.NotFound: + pass # Somehow it got deleted ? just ignore it + except discord.HTTPException as e: + log.error(f"Failed to pin coc session message {msg.id}", exc_info=e) + + coc_helper.message = msg + + @coc_session.command() + async def end(self, interaction: core.InteractionType): + """Ends the current coc session""" + + if not coc_helper.session: + return await interaction.response.send_message("There is no active clash of code session", ephemeral=True) + + staff_role = interaction.guild.get_role(settings.moderation.staff_role_id) + is_staff = staff_role in interaction.user.roles + + if coc_helper.host != interaction.user and not is_staff: + return await interaction.response.send_message( + "Only the session host or a staff member can end the session.", ephemeral=True + ) + + await interaction.response.defer() + + if coc_helper.message: + try: + view = CocMessageView() + button = discord.utils.get(view.children, custom_id=CocMessageView.JOIN_LEAVE_CUSTOM_ID) + button.disabled = True + await coc_helper.message.edit(view=view) + except discord.HTTPException as e: + log.error("Failed to edit message to disable button", exc_info=e) + + session_role = interaction.guild.get_role(settings.coc.session_role_id) + if session_role: + for member in session_role.members: + try: + await member.remove_roles(session_role) + except discord.HTTPException as e: + log.error(f"Failed to remove role from {member.display_name}", exc_info=e) + + try: + await coc_helper.message.unpin() + except discord.NotFound: + pass # Somehow it got deleted ? just ignore it + except discord.HTTPException as e: + log.error(f"Failed to unpin coc session message {coc_helper.message.id}", exc_info=e) + + coc_helper.last_clash = 0 + coc_helper.session = False + coc_helper.clash = None + coc_helper.message = None + coc_helper.host = None + coc_helper.languages = None + coc_helper.modes = None + coc_helper.handle = None + + await interaction.followup.send( + f"Clash session has been closed by {interaction.user.mention}. See you later", + allowed_mentions=discord.AllowedMentions(users=True), + ) + + coc_game = app_commands.Group(name="game", description="Game commands") + + @coc_game.command() + async def create(self, interaction: core.InteractionType): + """Creates a new Clash of Code game.""" + + if not coc_helper.session: + return await interaction.response.send_message( + "There is no active clash of code session. Do `/coc session new` to start one.", ephemeral=True + ) + + is_staff = settings.moderation.staff_role_id in [role.id for role in interaction.user.roles] + if interaction.user != coc_helper.host and not is_staff: + return await interaction.response.send_message( + "Only the session host or a staff member can create a game.", ephemeral=True + ) + + if coc_helper.handle: + return await interaction.response.send_message( + "A game has already been created for this session.", ephemeral=True + ) await interaction.response.send_message( - "Select the programming languages and modes you want to use", + "Select the programming languages and modes you want to use for the game", ephemeral=True, - view=self._create_coc_view, + view=CreateCocView(), ) - @app_commands.command() + @coc_game.command() async def start(self, interaction: core.InteractionType): - """Starts the current clash""" + """Starts the current clash game""" - if not coc_helper.session: - return await interaction.response.send_message("There is no active clash of code session", ephemeral=True) + if not coc_helper.handle: + return await interaction.response.send_message("There is no active clash of code game", ephemeral=True) if coc_helper.clash.started: return await interaction.response.send_message("The clash has already started", ephemeral=True) + await interaction.response.defer() + try: await coc_client.request( "ClashOfCode", "startClashByHandle", [coc_client.codingamer.id, coc_helper.clash.public_handle] ) except HTTPError as e: log.info("Handled error in /coc start : %s\n%s", e.reason, e.data) - return await interaction.response.send_message( + return await interaction.followup.send_message( "An error occurred while starting the clash. Please try again later", ephemeral=True ) except ContentTypeError: # Issue with the codingame library always assuming the response is JSON pass - await interaction.response.send_message( + await interaction.followup.send_message( "Clash started!", - ephemeral=False, + ephemeral=True, ) @app_commands.command() - async def end(self, interaction: core.InteractionType): - """Ends the current coc session""" + @app_commands.checks.cooldown(1, 10.0) + async def ping(self, interaction: core.InteractionType): + """Assigns/Removes coc notification role.""" - if not coc_helper.session: - return await interaction.response.send_message("There is no active clash of code session", ephemeral=True) + ping_role = interaction.guild.get_role(settings.coc.role_id) - coc_helper.last_clash = 0 - coc_helper.session = False - coc_helper.clash = None + if not ping_role: + return await interaction.response.send_message("The CoC notification role was not found.") - return await interaction.response.send_message( - f"Clash session has been closed by {interaction.user.mention}. See you later", - allowed_mentions=discord.AllowedMentions(users=True), - ) + await interaction.response.defer(ephemeral=True) + + if ping_role in interaction.user.roles: + try: + await interaction.user.remove_roles(ping_role) + await interaction.followup.send( + "Clash of Code session notifications **disabled**.\n" + "The CoC role has been removed — you will no longer receive session alerts.", + ephemeral=True, + ) + except discord.HTTPException as e: + log.error(f"Failed to remove role from {interaction.user.display_name}", exc_info=e) + else: + try: + await interaction.user.add_roles(ping_role) + await interaction.followup.send( + "Clash of Code session notifications **enabled**.\nYou've been assigned the CoC role.", + ephemeral=True, + ) + except discord.HTTPException as e: + log.error(f"Failed to add role to {interaction.user.display_name}", exc_info=e) + + async def cog_app_command_error(self, interaction: core.InteractionType, error: app_commands.AppCommandError): + if isinstance(error, app_commands.CommandOnCooldown): + seconds = int(error.retry_after) + if seconds < 60: + time_str = f"{seconds} second{'s' if seconds != 1 else ''}" + else: + minutes = seconds // 60 + remaining_seconds = seconds % 60 + time_str = f"{minutes} minute{'s' if minutes != 1 else ''}" + if remaining_seconds > 0: + time_str += f" and {remaining_seconds} second{'s' if remaining_seconds != 1 else ''}" + + await interaction.response.send_message( + f"This command is on cooldown. Please try again in {time_str}.", + ephemeral=True, + ) + else: + raise error + + +async def setup(bot: core.DiscordBot): + await bot.add_cog(ClashOfCode(bot=bot)) diff --git a/bot/extensions/clashofcode/utils.py b/bot/extensions/clashofcode/utils.py index 494e2d36..81b3ae8f 100644 --- a/bot/extensions/clashofcode/utils.py +++ b/bot/extensions/clashofcode/utils.py @@ -1,11 +1,21 @@ +import typing + import codingame +if typing.TYPE_CHECKING: + import discord + class ClashOfCodeHelper: def __init__(self): self.session = False self.clash = None self.last_clash = 0 + self.host: discord.Member | None = None + self.message: discord.Message | None = None + self.languages: list[str] | None = None + self.modes: list[str] | None = None + self.handle: str | None = None coc_helper = ClashOfCodeHelper() diff --git a/bot/extensions/clashofcode/views.py b/bot/extensions/clashofcode/views.py index 8a13d49e..f12404d5 100644 --- a/bot/extensions/clashofcode/views.py +++ b/bot/extensions/clashofcode/views.py @@ -5,7 +5,6 @@ import discord from aiohttp import ContentTypeError from codingame.http import HTTPError -from discord import ui from bot import core from bot.config import settings @@ -21,7 +20,50 @@ def em(mode: str, players: str): return embed -class CreateCocView(ui.View): +async def resolve_member(guild: discord.Guild, name: str) -> discord.Member | None: + """Case-insensitive search for a member in a guild by name or display name.""" + return discord.utils.find( + lambda m: m.display_name.lower() == name.lower() or m.name.lower() == name.lower(), guild.members + ) + + +class CocMessageView(discord.ui.View): + JOIN_LEAVE_CUSTOM_ID = "extensions:clashofcode:join_leave" + + def __init__(self): + super().__init__(timeout=None) + + @discord.ui.button( + label="Join/Leave Session", + style=discord.ButtonStyle.primary, + custom_id=JOIN_LEAVE_CUSTOM_ID, + ) + async def join_leave_session(self, interaction: core.InteractionType, button: discord.ui.Button): + if not coc_helper.session: + button.disabled = True + await interaction.response.edit_message(view=self) + return await interaction.followup.send("This clash of code session has already ended.", ephemeral=True) + + session_role = interaction.guild.get_role(settings.coc.session_role_id) + if not session_role: + await interaction.response.send_message("The session role could not be found.", ephemeral=True) + return + + if session_role in interaction.user.roles: + try: + await interaction.user.remove_roles(session_role) + await interaction.response.send_message("You have left the session.", ephemeral=True) + except discord.HTTPException: + await interaction.response.send_message("Failed to leave the session.", ephemeral=True) + else: + try: + await interaction.user.add_roles(session_role) + await interaction.response.send_message("You have joined the session.", ephemeral=True) + except discord.HTTPException: + await interaction.response.send_message("Failed to join the session.", ephemeral=True) + + +class CreateCocView(discord.ui.View): LANGUAGES_CUSTOM_ID = "extensions:clashofcode:languages" MODES_CUSTOM_ID = "extensions:clashofcode:modes" SELECT_ALL_CUSTOM_ID = "extensions:clashofcode:select_all" @@ -34,13 +76,13 @@ class CreateCocView(ui.View): options=[discord.SelectOption(label=lang, default=True) for lang in languages], max_values=len(languages), ) - async def select_languages(self, interaction: core.InteractionType, _select: ui.Select): + async def select_languages(self, interaction: core.InteractionType, _select: discord.ui.Select): await interaction.response.defer() @discord.ui.button( label="Select All", style=discord.ButtonStyle.blurple, emoji="🟢", custom_id=SELECT_ALL_CUSTOM_ID ) - async def select_all_languages(self, interaction: core.InteractionType, _button: ui.Button): + async def select_all_languages(self, interaction: core.InteractionType, _button: discord.ui.Button): modes_select = discord.utils.get(self.children, custom_id=self.MODES_CUSTOM_ID) for option in modes_select.options: option.default = not modes_select.values or option.label in modes_select.values @@ -53,7 +95,7 @@ async def select_all_languages(self, interaction: core.InteractionType, _button: @discord.ui.button( label="Deselect All", style=discord.ButtonStyle.blurple, emoji="🔴", custom_id=DESELECT_ALL_CUSTOM_ID ) - async def deselect_all_languages(self, interaction: core.InteractionType, _button: ui.Button): + async def deselect_all_languages(self, interaction: core.InteractionType, _button: discord.ui.Button): languages_select = discord.utils.get(self.children, custom_id=self.LANGUAGES_CUSTOM_ID) modes_select = discord.utils.get(self.children, custom_id=self.MODES_CUSTOM_ID) for option in languages_select.options: @@ -74,13 +116,19 @@ async def deselect_all_languages(self, interaction: core.InteractionType, _butto options=[discord.SelectOption(label=mode, default=True) for mode in modes], max_values=len(modes), ) - async def select_modes(self, interaction: core.InteractionType, _select: ui.Select): + async def select_modes(self, interaction: core.InteractionType, _select: discord.ui.Select): await interaction.response.defer() @discord.ui.button( - label="Create Clash of Code", style=discord.ButtonStyle.green, emoji="📝", custom_id=CREATE_CUSTOM_ID, row=3 + label="Create Game", style=discord.ButtonStyle.green, emoji="📝", custom_id=CREATE_CUSTOM_ID, row=3 ) - async def create_coc(self, interaction: core.InteractionType, _button: ui.Button): + async def create_coc(self, interaction: core.InteractionType, _button: discord.ui.Button): + is_staff = settings.moderation.staff_role_id in [role.id for role in interaction.user.roles] + if interaction.user != coc_helper.host and not is_staff: + return await interaction.response.send_message( + "Only the session host or a staff member can create a game.", ephemeral=True + ) + selected_languages = discord.utils.get(self.children, custom_id=self.LANGUAGES_CUSTOM_ID).values selected_modes = discord.utils.get(self.children, custom_id=self.MODES_CUSTOM_ID).values @@ -93,9 +141,8 @@ async def create_coc(self, interaction: core.InteractionType, _button: ui.Button await interaction.delete_original_response() # A session already exists, and it was recently created - if coc_helper.session and coc_helper.last_clash + 60 * 5 > int(time.time()): - await interaction.followup.send("A session has already been made!", ephemeral=True) - return + if coc_helper.handle and coc_helper.last_clash + 60 * 5 > int(time.time()): + return await interaction.followup.send("A game has already been created!", ephemeral=True) data = await coc_client.request( "ClashOfCode", @@ -103,6 +150,7 @@ async def create_coc(self, interaction: core.InteractionType, _button: ui.Button [coc_client.codingamer.id, selected_languages, selected_modes], ) handle = data["publicHandle"] + coc_helper.handle = handle msg = await interaction.channel.send( ( @@ -110,12 +158,11 @@ async def create_coc(self, interaction: core.InteractionType, _button: ui.Button f"Mode{'s' if len(selected_modes) > 1 else ''}: {', '.join(selected_modes).title()}\n" f"Programming languages: {', '.join(selected_languages) if selected_languages else 'All'}\n" f"Join here: \n" - f"{f'<@&{settings.coc.role_id}>' if not coc_helper.session else ''}" + f"<@&{settings.coc.session_role_id}>" ), allowed_mentions=discord.AllowedMentions(users=True, roles=True), ) - coc_helper.session = True coc_helper.last_clash = time.time() clash = await coc_client.get_clash_of_code(handle) @@ -128,7 +175,8 @@ async def create_coc(self, interaction: core.InteractionType, _button: ui.Button tries -= 1 if not clash.started: - coc_helper.session = False + coc_helper.clash = None + coc_helper.handle = None await msg.reply("Canceling clash due to lack of participants.") try: @@ -195,7 +243,8 @@ async def create_coc(self, interaction: core.InteractionType, _button: ui.Button players = [player.pseudo for player in clash.players if player.id != coc_client.codingamer.id] await start_message.edit(embed=em(clash.mode, ", ".join(players))) - coc_helper.session = False + coc_helper.clash = None + coc_helper.handle = None embed = discord.Embed( title="**Clash finished**", diff --git a/bot/models/migrations/006_up__configs.sql b/bot/models/migrations/006_up__configs.sql index af619dc8..f2eda076 100644 --- a/bot/models/migrations/006_up__configs.sql +++ b/bot/models/migrations/006_up__configs.sql @@ -7,11 +7,3 @@ CREATE TABLE IF NOT EXISTS guild_configs custom_role_log_channel_id BIGINT, divider_role_id BIGINT ); - - -ALTER TABLE migrations ALTER COLUMN id SET NOT NULL; -ALTER TABLE migrations ALTER COLUMN version SET NOT NULL; -ALTER TABLE migrations ALTER COLUMN direction SET NOT NULL; -ALTER TABLE migrations ALTER COLUMN name SET NOT NULL; -ALTER TABLE migrations ALTER COLUMN timestamp SET NOT NULL; -ALTER TABLE migrations ADD PRIMARY KEY (id); diff --git a/example.env b/example.env index 9a2e5fa6..0f27701e 100644 --- a/example.env +++ b/example.env @@ -25,6 +25,7 @@ CHALLENGES__WINNER_ROLE_ID= COC__CHANNEL_ID= COC__MESSAGE_ID= COC__ROLE_ID= +COC__SESSION_ROLE_ID= COC__SESSION_COOKIE= # --- Postgres diff --git a/poetry.lock b/poetry.lock index 30198fc3..0b09eaa7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.0 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -169,69 +169,78 @@ description = "Timeout context manager for asyncio programs" optional = false python-versions = ">=3.8" groups = ["main", "dev"] +markers = "python_version == \"3.10\"" files = [ {file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"}, {file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"}, ] -markers = {main = "python_version < \"3.12.0\"", dev = "python_version < \"3.11\""} [[package]] name = "asyncpg" -version = "0.29.0" +version = "0.30.0" description = "An asyncio PostgreSQL driver" optional = false python-versions = ">=3.8.0" groups = ["main"] files = [ - {file = "asyncpg-0.29.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72fd0ef9f00aeed37179c62282a3d14262dbbafb74ec0ba16e1b1864d8a12169"}, - {file = "asyncpg-0.29.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:52e8f8f9ff6e21f9b39ca9f8e3e33a5fcdceaf5667a8c5c32bee158e313be385"}, - {file = "asyncpg-0.29.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9e6823a7012be8b68301342ba33b4740e5a166f6bbda0aee32bc01638491a22"}, - {file = "asyncpg-0.29.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:746e80d83ad5d5464cfbf94315eb6744222ab00aa4e522b704322fb182b83610"}, - {file = "asyncpg-0.29.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ff8e8109cd6a46ff852a5e6bab8b0a047d7ea42fcb7ca5ae6eaae97d8eacf397"}, - {file = "asyncpg-0.29.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:97eb024685b1d7e72b1972863de527c11ff87960837919dac6e34754768098eb"}, - {file = "asyncpg-0.29.0-cp310-cp310-win32.whl", hash = "sha256:5bbb7f2cafd8d1fa3e65431833de2642f4b2124be61a449fa064e1a08d27e449"}, - {file = "asyncpg-0.29.0-cp310-cp310-win_amd64.whl", hash = "sha256:76c3ac6530904838a4b650b2880f8e7af938ee049e769ec2fba7cd66469d7772"}, - {file = "asyncpg-0.29.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4900ee08e85af01adb207519bb4e14b1cae8fd21e0ccf80fac6aa60b6da37b4"}, - {file = "asyncpg-0.29.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a65c1dcd820d5aea7c7d82a3fdcb70e096f8f70d1a8bf93eb458e49bfad036ac"}, - {file = "asyncpg-0.29.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b52e46f165585fd6af4863f268566668407c76b2c72d366bb8b522fa66f1870"}, - {file = "asyncpg-0.29.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc600ee8ef3dd38b8d67421359779f8ccec30b463e7aec7ed481c8346decf99f"}, - {file = "asyncpg-0.29.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:039a261af4f38f949095e1e780bae84a25ffe3e370175193174eb08d3cecab23"}, - {file = "asyncpg-0.29.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6feaf2d8f9138d190e5ec4390c1715c3e87b37715cd69b2c3dfca616134efd2b"}, - {file = "asyncpg-0.29.0-cp311-cp311-win32.whl", hash = "sha256:1e186427c88225ef730555f5fdda6c1812daa884064bfe6bc462fd3a71c4b675"}, - {file = "asyncpg-0.29.0-cp311-cp311-win_amd64.whl", hash = "sha256:cfe73ffae35f518cfd6e4e5f5abb2618ceb5ef02a2365ce64f132601000587d3"}, - {file = "asyncpg-0.29.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6011b0dc29886ab424dc042bf9eeb507670a3b40aece3439944006aafe023178"}, - {file = "asyncpg-0.29.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b544ffc66b039d5ec5a7454667f855f7fec08e0dfaf5a5490dfafbb7abbd2cfb"}, - {file = "asyncpg-0.29.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d84156d5fb530b06c493f9e7635aa18f518fa1d1395ef240d211cb563c4e2364"}, - {file = "asyncpg-0.29.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54858bc25b49d1114178d65a88e48ad50cb2b6f3e475caa0f0c092d5f527c106"}, - {file = "asyncpg-0.29.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bde17a1861cf10d5afce80a36fca736a86769ab3579532c03e45f83ba8a09c59"}, - {file = "asyncpg-0.29.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:37a2ec1b9ff88d8773d3eb6d3784dc7e3fee7756a5317b67f923172a4748a175"}, - {file = "asyncpg-0.29.0-cp312-cp312-win32.whl", hash = "sha256:bb1292d9fad43112a85e98ecdc2e051602bce97c199920586be83254d9dafc02"}, - {file = "asyncpg-0.29.0-cp312-cp312-win_amd64.whl", hash = "sha256:2245be8ec5047a605e0b454c894e54bf2ec787ac04b1cb7e0d3c67aa1e32f0fe"}, - {file = "asyncpg-0.29.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0009a300cae37b8c525e5b449233d59cd9868fd35431abc470a3e364d2b85cb9"}, - {file = "asyncpg-0.29.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cad1324dbb33f3ca0cd2074d5114354ed3be2b94d48ddfd88af75ebda7c43cc"}, - {file = "asyncpg-0.29.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:012d01df61e009015944ac7543d6ee30c2dc1eb2f6b10b62a3f598beb6531548"}, - {file = "asyncpg-0.29.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:000c996c53c04770798053e1730d34e30cb645ad95a63265aec82da9093d88e7"}, - {file = "asyncpg-0.29.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e0bfe9c4d3429706cf70d3249089de14d6a01192d617e9093a8e941fea8ee775"}, - {file = "asyncpg-0.29.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:642a36eb41b6313ffa328e8a5c5c2b5bea6ee138546c9c3cf1bffaad8ee36dd9"}, - {file = "asyncpg-0.29.0-cp38-cp38-win32.whl", hash = "sha256:a921372bbd0aa3a5822dd0409da61b4cd50df89ae85150149f8c119f23e8c408"}, - {file = "asyncpg-0.29.0-cp38-cp38-win_amd64.whl", hash = "sha256:103aad2b92d1506700cbf51cd8bb5441e7e72e87a7b3a2ca4e32c840f051a6a3"}, - {file = "asyncpg-0.29.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5340dd515d7e52f4c11ada32171d87c05570479dc01dc66d03ee3e150fb695da"}, - {file = "asyncpg-0.29.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e17b52c6cf83e170d3d865571ba574577ab8e533e7361a2b8ce6157d02c665d3"}, - {file = "asyncpg-0.29.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f100d23f273555f4b19b74a96840aa27b85e99ba4b1f18d4ebff0734e78dc090"}, - {file = "asyncpg-0.29.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48e7c58b516057126b363cec8ca02b804644fd012ef8e6c7e23386b7d5e6ce83"}, - {file = "asyncpg-0.29.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f9ea3f24eb4c49a615573724d88a48bd1b7821c890c2effe04f05382ed9e8810"}, - {file = "asyncpg-0.29.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8d36c7f14a22ec9e928f15f92a48207546ffe68bc412f3be718eedccdf10dc5c"}, - {file = "asyncpg-0.29.0-cp39-cp39-win32.whl", hash = "sha256:797ab8123ebaed304a1fad4d7576d5376c3a006a4100380fb9d517f0b59c1ab2"}, - {file = "asyncpg-0.29.0-cp39-cp39-win_amd64.whl", hash = "sha256:cce08a178858b426ae1aa8409b5cc171def45d4293626e7aa6510696d46decd8"}, - {file = "asyncpg-0.29.0.tar.gz", hash = "sha256:d1c49e1f44fffafd9a55e1a9b101590859d881d639ea2922516f5d9c512d354e"}, + {file = "asyncpg-0.30.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bfb4dd5ae0699bad2b233672c8fc5ccbd9ad24b89afded02341786887e37927e"}, + {file = "asyncpg-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc1f62c792752a49f88b7e6f774c26077091b44caceb1983509edc18a2222ec0"}, + {file = "asyncpg-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3152fef2e265c9c24eec4ee3d22b4f4d2703d30614b0b6753e9ed4115c8a146f"}, + {file = "asyncpg-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7255812ac85099a0e1ffb81b10dc477b9973345793776b128a23e60148dd1af"}, + {file = "asyncpg-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:578445f09f45d1ad7abddbff2a3c7f7c291738fdae0abffbeb737d3fc3ab8b75"}, + {file = "asyncpg-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c42f6bb65a277ce4d93f3fba46b91a265631c8df7250592dd4f11f8b0152150f"}, + {file = "asyncpg-0.30.0-cp310-cp310-win32.whl", hash = "sha256:aa403147d3e07a267ada2ae34dfc9324e67ccc4cdca35261c8c22792ba2b10cf"}, + {file = "asyncpg-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:fb622c94db4e13137c4c7f98834185049cc50ee01d8f657ef898b6407c7b9c50"}, + {file = "asyncpg-0.30.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5e0511ad3dec5f6b4f7a9e063591d407eee66b88c14e2ea636f187da1dcfff6a"}, + {file = "asyncpg-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:915aeb9f79316b43c3207363af12d0e6fd10776641a7de8a01212afd95bdf0ed"}, + {file = "asyncpg-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c198a00cce9506fcd0bf219a799f38ac7a237745e1d27f0e1f66d3707c84a5a"}, + {file = "asyncpg-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3326e6d7381799e9735ca2ec9fd7be4d5fef5dcbc3cb555d8a463d8460607956"}, + {file = "asyncpg-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:51da377487e249e35bd0859661f6ee2b81db11ad1f4fc036194bc9cb2ead5056"}, + {file = "asyncpg-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bc6d84136f9c4d24d358f3b02be4b6ba358abd09f80737d1ac7c444f36108454"}, + {file = "asyncpg-0.30.0-cp311-cp311-win32.whl", hash = "sha256:574156480df14f64c2d76450a3f3aaaf26105869cad3865041156b38459e935d"}, + {file = "asyncpg-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:3356637f0bd830407b5597317b3cb3571387ae52ddc3bca6233682be88bbbc1f"}, + {file = "asyncpg-0.30.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c902a60b52e506d38d7e80e0dd5399f657220f24635fee368117b8b5fce1142e"}, + {file = "asyncpg-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aca1548e43bbb9f0f627a04666fedaca23db0a31a84136ad1f868cb15deb6e3a"}, + {file = "asyncpg-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c2a2ef565400234a633da0eafdce27e843836256d40705d83ab7ec42074efb3"}, + {file = "asyncpg-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1292b84ee06ac8a2ad8e51c7475aa309245874b61333d97411aab835c4a2f737"}, + {file = "asyncpg-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0f5712350388d0cd0615caec629ad53c81e506b1abaaf8d14c93f54b35e3595a"}, + {file = "asyncpg-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:db9891e2d76e6f425746c5d2da01921e9a16b5a71a1c905b13f30e12a257c4af"}, + {file = "asyncpg-0.30.0-cp312-cp312-win32.whl", hash = "sha256:68d71a1be3d83d0570049cd1654a9bdfe506e794ecc98ad0873304a9f35e411e"}, + {file = "asyncpg-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:9a0292c6af5c500523949155ec17b7fe01a00ace33b68a476d6b5059f9630305"}, + {file = "asyncpg-0.30.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:05b185ebb8083c8568ea8a40e896d5f7af4b8554b64d7719c0eaa1eb5a5c3a70"}, + {file = "asyncpg-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c47806b1a8cbb0a0db896f4cd34d89942effe353a5035c62734ab13b9f938da3"}, + {file = "asyncpg-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b6fde867a74e8c76c71e2f64f80c64c0f3163e687f1763cfaf21633ec24ec33"}, + {file = "asyncpg-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46973045b567972128a27d40001124fbc821c87a6cade040cfcd4fa8a30bcdc4"}, + {file = "asyncpg-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9110df111cabc2ed81aad2f35394a00cadf4f2e0635603db6ebbd0fc896f46a4"}, + {file = "asyncpg-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:04ff0785ae7eed6cc138e73fc67b8e51d54ee7a3ce9b63666ce55a0bf095f7ba"}, + {file = "asyncpg-0.30.0-cp313-cp313-win32.whl", hash = "sha256:ae374585f51c2b444510cdf3595b97ece4f233fde739aa14b50e0d64e8a7a590"}, + {file = "asyncpg-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:f59b430b8e27557c3fb9869222559f7417ced18688375825f8f12302c34e915e"}, + {file = "asyncpg-0.30.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:29ff1fc8b5bf724273782ff8b4f57b0f8220a1b2324184846b39d1ab4122031d"}, + {file = "asyncpg-0.30.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:64e899bce0600871b55368b8483e5e3e7f1860c9482e7f12e0a771e747988168"}, + {file = "asyncpg-0.30.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b290f4726a887f75dcd1b3006f484252db37602313f806e9ffc4e5996cfe5cb"}, + {file = "asyncpg-0.30.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f86b0e2cd3f1249d6fe6fd6cfe0cd4538ba994e2d8249c0491925629b9104d0f"}, + {file = "asyncpg-0.30.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:393af4e3214c8fa4c7b86da6364384c0d1b3298d45803375572f415b6f673f38"}, + {file = "asyncpg-0.30.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:fd4406d09208d5b4a14db9a9dbb311b6d7aeeab57bded7ed2f8ea41aeef39b34"}, + {file = "asyncpg-0.30.0-cp38-cp38-win32.whl", hash = "sha256:0b448f0150e1c3b96cb0438a0d0aa4871f1472e58de14a3ec320dbb2798fb0d4"}, + {file = "asyncpg-0.30.0-cp38-cp38-win_amd64.whl", hash = "sha256:f23b836dd90bea21104f69547923a02b167d999ce053f3d502081acea2fba15b"}, + {file = "asyncpg-0.30.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6f4e83f067b35ab5e6371f8a4c93296e0439857b4569850b178a01385e82e9ad"}, + {file = "asyncpg-0.30.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5df69d55add4efcd25ea2a3b02025b669a285b767bfbf06e356d68dbce4234ff"}, + {file = "asyncpg-0.30.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3479a0d9a852c7c84e822c073622baca862d1217b10a02dd57ee4a7a081f708"}, + {file = "asyncpg-0.30.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26683d3b9a62836fad771a18ecf4659a30f348a561279d6227dab96182f46144"}, + {file = "asyncpg-0.30.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1b982daf2441a0ed314bd10817f1606f1c28b1136abd9e4f11335358c2c631cb"}, + {file = "asyncpg-0.30.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1c06a3a50d014b303e5f6fc1e5f95eb28d2cee89cf58384b700da621e5d5e547"}, + {file = "asyncpg-0.30.0-cp39-cp39-win32.whl", hash = "sha256:1b11a555a198b08f5c4baa8f8231c74a366d190755aa4f99aacec5970afe929a"}, + {file = "asyncpg-0.30.0-cp39-cp39-win_amd64.whl", hash = "sha256:8b684a3c858a83cd876f05958823b68e8d14ec01bb0c0d14a6704c5bf9711773"}, + {file = "asyncpg-0.30.0.tar.gz", hash = "sha256:c551e9928ab6707602f44811817f82ba3c446e018bfe1d3abecc8ba5f3eac851"}, ] [package.dependencies] -async-timeout = {version = ">=4.0.3", markers = "python_version < \"3.12.0\""} +async-timeout = {version = ">=4.0.3", markers = "python_version < \"3.11.0\""} [package.extras] -docs = ["Sphinx (>=5.3.0,<5.4.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] -test = ["flake8 (>=6.1,<7.0)", "uvloop (>=0.15.3) ; platform_system != \"Windows\" and python_version < \"3.12.0\""] +docs = ["Sphinx (>=8.1.3,<8.2.0)", "sphinx-rtd-theme (>=1.2.2)"] +gssauth = ["gssapi ; platform_system != \"Windows\"", "sspilib ; platform_system == \"Windows\""] +test = ["distro (>=1.9.0,<1.10.0)", "flake8 (>=6.1,<7.0)", "flake8-pyi (>=24.1.0,<24.2.0)", "gssapi ; platform_system == \"Linux\"", "k5test ; platform_system == \"Linux\"", "mypy (>=1.8.0,<1.9.0)", "sspilib ; platform_system == \"Windows\"", "uvloop (>=0.15.3) ; platform_system != \"Windows\" and python_version < \"3.14.0\""] [[package]] name = "attrs" @@ -253,6 +262,50 @@ docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphi tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""] +[[package]] +name = "audioop-lts" +version = "0.2.1" +description = "LTS Port of Python audioop" +optional = false +python-versions = ">=3.13" +groups = ["main"] +markers = "python_version >= \"3.13\"" +files = [ + {file = "audioop_lts-0.2.1-cp313-abi3-macosx_10_13_universal2.whl", hash = "sha256:fd1345ae99e17e6910f47ce7d52673c6a1a70820d78b67de1b7abb3af29c426a"}, + {file = "audioop_lts-0.2.1-cp313-abi3-macosx_10_13_x86_64.whl", hash = "sha256:e175350da05d2087e12cea8e72a70a1a8b14a17e92ed2022952a4419689ede5e"}, + {file = "audioop_lts-0.2.1-cp313-abi3-macosx_11_0_arm64.whl", hash = "sha256:4a8dd6a81770f6ecf019c4b6d659e000dc26571b273953cef7cd1d5ce2ff3ae6"}, + {file = "audioop_lts-0.2.1-cp313-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1cd3c0b6f2ca25c7d2b1c3adeecbe23e65689839ba73331ebc7d893fcda7ffe"}, + {file = "audioop_lts-0.2.1-cp313-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ff3f97b3372c97782e9c6d3d7fdbe83bce8f70de719605bd7ee1839cd1ab360a"}, + {file = "audioop_lts-0.2.1-cp313-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a351af79edefc2a1bd2234bfd8b339935f389209943043913a919df4b0f13300"}, + {file = "audioop_lts-0.2.1-cp313-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2aeb6f96f7f6da80354330470b9134d81b4cf544cdd1c549f2f45fe964d28059"}, + {file = "audioop_lts-0.2.1-cp313-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c589f06407e8340e81962575fcffbba1e92671879a221186c3d4662de9fe804e"}, + {file = "audioop_lts-0.2.1-cp313-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fbae5d6925d7c26e712f0beda5ed69ebb40e14212c185d129b8dfbfcc335eb48"}, + {file = "audioop_lts-0.2.1-cp313-abi3-musllinux_1_2_i686.whl", hash = "sha256:d2d5434717f33117f29b5691fbdf142d36573d751716249a288fbb96ba26a281"}, + {file = "audioop_lts-0.2.1-cp313-abi3-musllinux_1_2_ppc64le.whl", hash = "sha256:f626a01c0a186b08f7ff61431c01c055961ee28769591efa8800beadd27a2959"}, + {file = "audioop_lts-0.2.1-cp313-abi3-musllinux_1_2_s390x.whl", hash = "sha256:05da64e73837f88ee5c6217d732d2584cf638003ac72df124740460531e95e47"}, + {file = "audioop_lts-0.2.1-cp313-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:56b7a0a4dba8e353436f31a932f3045d108a67b5943b30f85a5563f4d8488d77"}, + {file = "audioop_lts-0.2.1-cp313-abi3-win32.whl", hash = "sha256:6e899eb8874dc2413b11926b5fb3857ec0ab55222840e38016a6ba2ea9b7d5e3"}, + {file = "audioop_lts-0.2.1-cp313-abi3-win_amd64.whl", hash = "sha256:64562c5c771fb0a8b6262829b9b4f37a7b886c01b4d3ecdbae1d629717db08b4"}, + {file = "audioop_lts-0.2.1-cp313-abi3-win_arm64.whl", hash = "sha256:c45317debeb64002e980077642afbd977773a25fa3dfd7ed0c84dccfc1fafcb0"}, + {file = "audioop_lts-0.2.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:3827e3fce6fee4d69d96a3d00cd2ab07f3c0d844cb1e44e26f719b34a5b15455"}, + {file = "audioop_lts-0.2.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:161249db9343b3c9780ca92c0be0d1ccbfecdbccac6844f3d0d44b9c4a00a17f"}, + {file = "audioop_lts-0.2.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5b7b4ff9de7a44e0ad2618afdc2ac920b91f4a6d3509520ee65339d4acde5abf"}, + {file = "audioop_lts-0.2.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72e37f416adb43b0ced93419de0122b42753ee74e87070777b53c5d2241e7fab"}, + {file = "audioop_lts-0.2.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:534ce808e6bab6adb65548723c8cbe189a3379245db89b9d555c4210b4aaa9b6"}, + {file = "audioop_lts-0.2.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d2de9b6fb8b1cf9f03990b299a9112bfdf8b86b6987003ca9e8a6c4f56d39543"}, + {file = "audioop_lts-0.2.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f24865991b5ed4b038add5edbf424639d1358144f4e2a3e7a84bc6ba23e35074"}, + {file = "audioop_lts-0.2.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bdb3b7912ccd57ea53197943f1bbc67262dcf29802c4a6df79ec1c715d45a78"}, + {file = "audioop_lts-0.2.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:120678b208cca1158f0a12d667af592e067f7a50df9adc4dc8f6ad8d065a93fb"}, + {file = "audioop_lts-0.2.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:54cd4520fc830b23c7d223693ed3e1b4d464997dd3abc7c15dce9a1f9bd76ab2"}, + {file = "audioop_lts-0.2.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:d6bd20c7a10abcb0fb3d8aaa7508c0bf3d40dfad7515c572014da4b979d3310a"}, + {file = "audioop_lts-0.2.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:f0ed1ad9bd862539ea875fb339ecb18fcc4148f8d9908f4502df28f94d23491a"}, + {file = "audioop_lts-0.2.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e1af3ff32b8c38a7d900382646e91f2fc515fd19dea37e9392275a5cbfdbff63"}, + {file = "audioop_lts-0.2.1-cp313-cp313t-win32.whl", hash = "sha256:f51bb55122a89f7a0817d7ac2319744b4640b5b446c4c3efcea5764ea99ae509"}, + {file = "audioop_lts-0.2.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f0f2f336aa2aee2bce0b0dcc32bbba9178995454c7b979cf6ce086a8801e14c7"}, + {file = "audioop_lts-0.2.1-cp313-cp313t-win_arm64.whl", hash = "sha256:78bfb3703388c780edf900be66e07de5a3d4105ca8e8720c5c4d67927e0b15d0"}, + {file = "audioop_lts-0.2.1.tar.gz", hash = "sha256:e81268da0baa880431b68b1308ab7257eb33f356e57a5f9b1f915dfb13dd1387"}, +] + [[package]] name = "beautifulsoup4" version = "4.13.3" @@ -510,23 +563,25 @@ files = [ [[package]] name = "discord-py" -version = "2.3.2" +version = "2.5.2" description = "A Python wrapper for the Discord API" optional = false -python-versions = ">=3.8.0" +python-versions = ">=3.8" groups = ["main"] files = [ - {file = "discord.py-2.3.2-py3-none-any.whl", hash = "sha256:9da4679fc3cb10c64b388284700dc998663e0e57328283bbfcfc2525ec5960a6"}, - {file = "discord.py-2.3.2.tar.gz", hash = "sha256:4560f70f2eddba7e83370ecebd237ac09fbb4980dc66507482b0c0e5b8f76b9c"}, + {file = "discord_py-2.5.2-py3-none-any.whl", hash = "sha256:81f23a17c50509ffebe0668441cb80c139e74da5115305f70e27ce821361295a"}, + {file = "discord_py-2.5.2.tar.gz", hash = "sha256:01cd362023bfea1a4a1d43f5280b5ef00cad2c7eba80098909f98bf28e578524"}, ] [package.dependencies] aiohttp = ">=3.7.4,<4" +audioop-lts = {version = "*", markers = "python_version >= \"3.13\""} [package.extras] -docs = ["sphinx (==4.4.0)", "sphinxcontrib-trio (==1.1.2)", "sphinxcontrib-websupport", "typing-extensions (>=4.3,<5)"] -speed = ["Brotli", "aiodns (>=1.1)", "cchardet (==2.1.7) ; python_version < \"3.10\"", "orjson (>=3.5.4)"] -test = ["coverage[toml]", "pytest", "pytest-asyncio", "pytest-cov", "pytest-mock", "typing-extensions (>=4.3,<5)"] +dev = ["black (==22.6)", "typing_extensions (>=4.3,<5)"] +docs = ["imghdr-lts (==1.0.0) ; python_version >= \"3.13\"", "sphinx (==4.4.0)", "sphinx-inline-tabs (==2023.4.21)", "sphinxcontrib-applehelp (==1.0.4)", "sphinxcontrib-devhelp (==1.0.2)", "sphinxcontrib-htmlhelp (==2.0.1)", "sphinxcontrib-jsmath (==1.0.1)", "sphinxcontrib-qthelp (==1.0.3)", "sphinxcontrib-serializinghtml (==1.1.5)", "sphinxcontrib-websupport (==1.2.4)", "sphinxcontrib_trio (==1.1.2)", "typing-extensions (>=4.3,<5)"] +speed = ["Brotli", "aiodns (>=1.1) ; sys_platform != \"win32\"", "cchardet (==2.1.7) ; python_version < \"3.10\"", "orjson (>=3.5.4)", "zstandard (>=0.23.0)"] +test = ["coverage[toml]", "pytest", "pytest-asyncio", "pytest-cov", "pytest-mock", "typing-extensions (>=4.3,<5)", "tzdata ; sys_platform == \"win32\""] voice = ["PyNaCl (>=1.3.0,<1.6)"] [[package]] @@ -1505,7 +1560,7 @@ description = "A lil' TOML parser" optional = false python-versions = ">=3.8" groups = ["dev"] -markers = "python_version < \"3.11\"" +markers = "python_version == \"3.10\"" files = [ {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, @@ -1552,7 +1607,7 @@ files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] -markers = {dev = "python_version < \"3.11\""} +markers = {dev = "python_version == \"3.10\""} [[package]] name = "tzdata" @@ -1720,4 +1775,4 @@ propcache = ">=0.2.0" [metadata] lock-version = "2.1" python-versions = "^3.10" -content-hash = "339ce1f68b3b33905537b1db4e49ee992c8d481ee8fc1f052cccbf6fc68ae385" +content-hash = "c31d141948a3e33c13adf8dc4cec283cfd49c96189a8ce306e70d0c3e4d582e3" diff --git a/pyproject.toml b/pyproject.toml index 5e4c73f9..871fa011 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,8 +11,8 @@ bot = "cli:main" [tool.poetry.dependencies] python = "^3.10" click = "^8.1.3" -asyncpg = "0.29.0" -discord-py = "2.3.2" +asyncpg = "^0.30.0" +discord-py = "^2.5.2" python-dateutil = "^2.8.2" pydantic = {extras = ["dotenv"], version = "^1.10.7"} jishaku = {extras = ["procinfo", "profiling"], version = "^2.5.1"}