From 703a9aef34a3796732925fe0f032274934dd076a Mon Sep 17 00:00:00 2001 From: Paillat-dev Date: Wed, 23 Apr 2025 17:59:51 +0200 Subject: [PATCH] First Commit --- .copywrite.hcl | 16 + .github/workflows/CI.yaml | 25 ++ .github/workflows/publish.yaml | 31 ++ .github/workflows/quality.yaml | 54 ++++ .gitignore | 173 +++++++++++ .pre-commit-config.yaml | 34 +++ README.md | 363 +++++++++++++++++++++++ examples/basic.py | 40 +++ pyproject.toml | 124 ++++++++ renovate.json | 25 ++ src/discord_progress_bar/__init__.py | 13 + src/discord_progress_bar/default_bars.py | 16 + src/discord_progress_bar/progress_bar.py | 115 +++++++ src/discord_progress_bar/py.typed | 0 src/discord_progress_bar/types.py | 27 ++ uv.lock | 316 ++++++++++++++++++++ 16 files changed, 1372 insertions(+) create mode 100644 .copywrite.hcl create mode 100644 .github/workflows/CI.yaml create mode 100644 .github/workflows/publish.yaml create mode 100644 .github/workflows/quality.yaml create mode 100644 .gitignore create mode 100644 .pre-commit-config.yaml create mode 100644 README.md create mode 100644 examples/basic.py create mode 100644 pyproject.toml create mode 100644 renovate.json create mode 100644 src/discord_progress_bar/__init__.py create mode 100644 src/discord_progress_bar/default_bars.py create mode 100644 src/discord_progress_bar/progress_bar.py create mode 100644 src/discord_progress_bar/py.typed create mode 100644 src/discord_progress_bar/types.py create mode 100644 uv.lock diff --git a/.copywrite.hcl b/.copywrite.hcl new file mode 100644 index 0000000..c79b19d --- /dev/null +++ b/.copywrite.hcl @@ -0,0 +1,16 @@ +schema_version = 1 + +project { + license = "MIT" + copyright_year = 2025 + copyright_holder = "Paillat-dev" + header_ignore = [ + ".venv/**", + "logs/**", + ".idea/**", + ".git/**", + ".vscode/**", + "__pycache__/**", + "*.pyc", + ] +} diff --git a/.github/workflows/CI.yaml b/.github/workflows/CI.yaml new file mode 100644 index 0000000..0a45ff9 --- /dev/null +++ b/.github/workflows/CI.yaml @@ -0,0 +1,25 @@ +name: CI + +on: + push: + branches: [ "main", "dev" ] + # Publish semver tags as releases. + tags: [ 'v*.*.*' ] + pull_request: + branches: ["main", "dev"] + release: + types: [created] + +jobs: + quality: + uses: ./.github/workflows/quality.yaml + permissions: + contents: read + + publish: + needs: quality + if: github.event_name == 'release' + uses: ./.github/workflows/publish.yaml + permissions: + id-token: write + contents: read \ No newline at end of file diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml new file mode 100644 index 0000000..e1e6319 --- /dev/null +++ b/.github/workflows/publish.yaml @@ -0,0 +1,31 @@ +name: Quality Checks + +on: + workflow_call: + +jobs: + publish: + name: Publish to PyPI + runs-on: ubuntu-latest + environment: pypi + steps: + - uses: actions/checkout@v4 + + - name: "Install uv" + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + + - name: "Set up Python" + uses: actions/setup-python@v5 + with: + python-version-file: "pyproject.toml" + + - name: Install dependencies + run: uv sync + + - name: Build + run: uv build + + - name: Publish + run: uv publish \ No newline at end of file diff --git a/.github/workflows/quality.yaml b/.github/workflows/quality.yaml new file mode 100644 index 0000000..153579f --- /dev/null +++ b/.github/workflows/quality.yaml @@ -0,0 +1,54 @@ +name: Quality Checks + +on: + workflow_call: + +jobs: + check-license-header: + name: License Header Check + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Setup Copywrite + uses: hashicorp/setup-copywrite@5e3e8a26d7b9f8a508848ad0a069dfd2f7aa5339 + - name: Check Header Compliance + run: copywrite headers --plan --config .copywrite.hcl + + quality: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + check: [format, lint, basedpyright] + include: + - check: format + name: "Format Check" + command: "uv run ruff format --check ." + - check: lint + name: "Lint Check" + command: "uv run ruff check ." + - check: basedpyright + name: "Type Check" + command: "uv run basedpyright ." + + name: ${{ matrix.name }} + + steps: + - uses: actions/checkout@v4 + + - name: "Install uv" + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + + - name: "Set up Python" + uses: actions/setup-python@v5 + with: + python-version-file: "pyproject.toml" + + - name: Install dependencies + run: uv sync + + - name: ${{ matrix.name }} + run: ${{ matrix.command }} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..59ab809 --- /dev/null +++ b/.gitignore @@ -0,0 +1,173 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# UV +# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +#uv.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +.idea/ + +# PyPI configuration file +.pypirc + +_version.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..66f3e29 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,34 @@ +# Copyright (c) NiceBots +# SPDX-License-Identifier: MIT + +ci: + autoupdate_commit_msg: ":construction_worker: pre-commit autoupdate" + autofix_commit_msg: ":art: auto fixes from pre-commit.com hooks" + +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: trailing-whitespace + exclude: \.(po|pot|yml|yaml)$ + - id: end-of-file-fixer + exclude: \.(po|pot|yml|yaml)$ + - repo: https://github.com/pre-commit/mirrors-prettier + rev: v4.0.0-alpha.8 + hooks: + - id: prettier + args: [--prose-wrap=always, --print-width=88] + exclude: \.(po|pot|yml|yaml)$ + - repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version. + rev: v0.9.10 + hooks: + # Run the linter. + - id: ruff + args: [ --fix ] + # Run the formatter. + - id: ruff-format + - repo: https://github.com/bhundven/copywrite # waiting for https://github.com/hashicorp/copywrite/pull/120 to be merged + rev: 937f17f09c46992447dfa8977bb96eda512588c4 + hooks: + - id: add-headers \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..fc25e65 --- /dev/null +++ b/README.md @@ -0,0 +1,363 @@ +
+

Discord Progress Bar

+ + + +![PyPI - Version](https://img.shields.io/pypi/v/discord-progress-bar) +![PyPI - Python Version](https://img.shields.io/pypi/pyversions/discord-progress-bar) +![PyPI - Types](https://img.shields.io/pypi/types/discord-progress-bar) +![PyPI - License](https://img.shields.io/pypi/l/discord-progress-bar) +![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/Paillat-dev/discord-progress-bar/CI.yaml) +[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/Paillat-dev/discord-progress-bar/main.svg)](https://results.pre-commit.ci/latest/github/Paillat-dev/discord-progress-bar/main) + + + +A Python library for creating customizable progress bars with Discord emojis in your +Discord bot messages. + +
+ +## Table of Contents + +- [Overview](#overview) +- [Installation](#installation) +- [Quick Start](#quick-start) +- [Core Concepts](#core-concepts) + - [How It Works](#how-it-works) + - [Key Components](#key-components) +- [Features](#features) + - [Type Safety](#type-safety) +- [Usage Examples](#usage-examples) + - [Basic Progress Bar](#basic-progress-bar) + - [Different Progress Bar States](#different-progress-bar-states) + - [Custom Progress Bar Length](#custom-progress-bar-length) +- [Configuration](#configuration) +- [Limitations](#limitations) +- [Getting Help](#getting-help) +- [Development](#development) + - [Contributing](#contributing) +- [License](#license) + +## Overview + +Discord Progress Bar is a Python library that allows you to create visually appealing +progress bars in your Discord bot messages using custom emojis. It provides a simple API +to generate progress bars of different styles and lengths, making it easy to display +progress, loading states, or completion percentages in your Discord applications. + +Built on: + +- [py-cord](https://github.com/Pycord-Development/pycord) - A modern, easy-to-use, + feature-rich, and async-ready API wrapper for Discord +- Custom Discord emojis - Used to create visually consistent progress bar segments + +## Installation + +```bash +pip install discord-progress-bar --prerelease=allow +``` + + +> [!NOTE] +> The package is currently in pre-release. + +## Quick Start + + + +> [!TIP] +> Create beautiful progress bars in your Discord bot with just a few lines of code! + +```python +import discord +from discord_progress_bar import ProgressBarManager + +# Create a Discord bot with emoji caching enabled +bot = discord.Bot(cache_app_emojis=True) + +@bot.event +async def on_ready(): + # Initialize the progress bar manager + progress_bar_manager = await ProgressBarManager(bot) + # Get a progress bar with the "green" style + progress_bar = await progress_bar_manager.progress_bar("green") + + # Use it in your commands + @bot.slash_command() + async def show_progress(ctx, percent: float = 0.5): + await ctx.respond(f"Progress: {progress_bar.partial(percent)}") + +# Run your bot +bot.run("YOUR_TOKEN") +``` + + +> [!NOTE] +> For a complete example, check the [examples directory](/examples). + +## Core Concepts + +### How It Works + +Discord Progress Bar works by using custom Discord emojis to create visually appealing +progress bars in your bot messages. Here's how it works: + +1. **Progress Bar Structure**: Each progress bar consists of multiple emoji segments: + +- Left edge (filled or empty) +- Middle sections (filled or empty) +- Right edge (filled or empty) + +2. **Emoji Management**: The `ProgressBarManager` class handles: + +- Loading existing emojis from your bot +- Creating new emojis from URLs or files if needed +- Providing the appropriate emojis to the `ProgressBar` class + +3. **Progress Bar Rendering**: The `ProgressBar` class handles: + +- Rendering full progress bars (100%) +- Rendering empty progress bars (0%) +- Rendering partial progress bars (any percentage) + +4. **Default Styles**: The library comes with a default "green" style, but you can + create custom styles by providing your own emoji images. + +### Key Components + +- **ProgressBarManager**: Initializes with your Discord bot and manages emoji resources +- **ProgressBar**: Renders progress bars with different percentages +- **ProgressBarPart**: Enum defining the different parts of a progress bar (LEFT_EMPTY, + LEFT_FILLED, etc.) +- **Default Bars**: Pre-configured progress bar styles that can be used out of the box + +## Features + +- **Easy Integration**: Seamlessly integrates with Discord bots built using py-cord +- **Customizable Progress Bars**: Create progress bars with different styles and lengths +- **Default Styles**: Comes with a pre-configured "green" style ready to use +- **Custom Styles**: Create your own progress bar styles using custom emoji images +- **Flexible Rendering**: Render progress bars at any percentage (0-100%) +- **Async Support**: Fully supports asynchronous operations for Discord bots +- **Emoji Management**: Automatically handles emoji creation and management + +### Type Safety + +Discord Progress Bar is fully type-annotated and type-safe. It uses `basedpyright` for +type checking. + + +> [!NOTE] +> While Discord Progress Bar itself is fully typed, the underlying py-cord library has limited type annotations, which may affect type checking in some areas. + +## Usage Examples + +### Basic Progress Bar + +```python +@discord.slash_command() +async def show_progress(self, ctx: discord.ApplicationContext, percent: float = 0.5) -> None: + """Display a progress bar with the specified percentage.""" + # Get a progress bar with the default "green" style + progress_bar = await self.progress_bar_manager.progress_bar("green", length=10) + # Render the progress bar at the specified percentage + await ctx.respond(f"Progress: {progress_bar.partial(percent)}") +``` + +### Different Progress Bar States + +```python +@discord.slash_command() +async def show_progress_states(self, ctx: discord.ApplicationContext) -> None: + """Display different progress bar states.""" + progress_bar = await self.progress_bar_manager.progress_bar("green", length=10) + + # Empty progress bar (0%) + empty = progress_bar.empty() + + # Partial progress bar (50%) + half = progress_bar.partial(0.5) + + # Full progress bar (100%) + full = progress_bar.full() + + await ctx.respond(f"Empty: {empty}\nHalf: {half}\nFull: {full}") +``` + +### Custom Progress Bar Length + +```python +@discord.slash_command() +async def show_different_lengths(self, ctx: discord.ApplicationContext) -> None: + """Display progress bars with different lengths.""" + # Short progress bar (5 segments) + short_bar = await self.progress_bar_manager.progress_bar("green", length=5) + + # Medium progress bar (10 segments) + medium_bar = await self.progress_bar_manager.progress_bar("green", length=10) + + # Long progress bar (20 segments) + long_bar = await self.progress_bar_manager.progress_bar("green", length=20) + + await ctx.respond( + f"Short (5): {short_bar.partial(0.7)}\n" + f"Medium (10): {medium_bar.partial(0.7)}\n" + f"Long (20): {long_bar.partial(0.7)}" + ) +``` + +For more examples, check the [examples directory](/examples) in the repository. + +## Configuration + +### Bot Configuration + +When creating your Discord bot, make sure to enable emoji caching: + +```python +bot = discord.Bot(cache_app_emojis=True) +``` + +This is required for the `ProgressBarManager` to properly load and manage emojis. + +### Progress Bar Manager Initialization + +Initialize the `ProgressBarManager` after your bot is ready: + +```python +@discord.Cog.listener() +async def on_ready(self) -> None: + self.progress_bar_manager = await ProgressBarManager(self.bot) +``` + +### Custom Progress Bar Styles + +You can create custom progress bar styles by providing your own emoji images: + +#### From URLs + +```python +from discord_progress_bar import ProgressBarPart + +# Define URLs for each part of the progress bar +custom_style = { + ProgressBarPart.LEFT_EMPTY: "https://example.com/left_empty.png", + ProgressBarPart.LEFT_FILLED: "https://example.com/left_filled.png", + ProgressBarPart.MIDDLE_EMPTY: "https://example.com/middle_empty.png", + ProgressBarPart.MIDDLE_FILLED: "https://example.com/middle_filled.png", + ProgressBarPart.RIGHT_EMPTY: "https://example.com/right_empty.png", + ProgressBarPart.RIGHT_FILLED: "https://example.com/right_filled.png", +} + +# Create emojis from URLs +await self.progress_bar_manager.create_emojis_from_urls("custom_style", custom_style) +``` + +#### From Files + +```python +import pathlib +from discord_progress_bar import ProgressBarPart + +# Define file paths for each part of the progress bar +custom_style = { + ProgressBarPart.LEFT_EMPTY: pathlib.Path("path/to/left_empty.png"), + ProgressBarPart.LEFT_FILLED: pathlib.Path("path/to/left_filled.png"), + ProgressBarPart.MIDDLE_EMPTY: pathlib.Path("path/to/middle_empty.png"), + ProgressBarPart.MIDDLE_FILLED: pathlib.Path("path/to/middle_filled.png"), + ProgressBarPart.RIGHT_EMPTY: pathlib.Path("path/to/right_empty.png"), + ProgressBarPart.RIGHT_FILLED: pathlib.Path("path/to/right_filled.png"), +} + +# Create emojis from files +await self.progress_bar_manager.create_emojis_from_files("custom_style", custom_style) +``` + +## Limitations + + +> [!WARNING] +> Please be aware of the following limitations: +> +> - **Python Version**: Requires Python 3.12 only +> - **Discord Bot Framework**: Currently only supports py-cord, not discord.py or other Discord API wrappers +> - **Emoji Creation**: Requires bot permissions to create and manage emojis in at least one server +> - **Emoji Limits**: Subject to Discord's emoji limits (50 custom emojis for regular servers, 200 for servers with Nitro boosts) +> - **Pre-release Status**: This package is currently in alpha stage and may have unexpected behaviors or breaking changes in future versions +> - **Custom Styles**: Creating custom styles requires providing all six emoji parts (LEFT_EMPTY, LEFT_FILLED, MIDDLE_EMPTY, MIDDLE_FILLED, RIGHT_EMPTY, RIGHT_FILLED) + +## Getting Help + +If you encounter issues or have questions about discord-progress-bar: + +- **GitHub Issues**: + [Submit a bug report or feature request](https://github.com/Paillat-dev/discord-progress-bar/issues) +- **Discord Support**: + - For py-cord related questions: Join the + [Pycord Official Server](https://discord.gg/pycord) + - For discord-progress-bar specific help: Join the + [Pycord Official Server](https://discord.gg/pycord) and mention `@paillat` + + +> [!TIP] +> Before asking for help, check if your question is already answered in the [examples directory](/examples) or existing GitHub issues. + +## Development + +### Local Testing + +To set up a local development environment: + +1. Clone the repository: + + ```bash + git clone https://github.com/Paillat-dev/discord-progress-bar.git + cd discord-progress-bar + ``` + +2. Create a virtual environment and install dependencies: + + ```bash + python -m venv .venv + source .venv/bin/activate # On Windows: .venv\Scripts\activate + pip install -e ".[dev]" # Install the package in development mode with dev dependencies + ``` + +3. Create a `.env` file with your Discord bot token: + + ``` + DISCORD_TOKEN=your_discord_bot_token + ``` + +4. Run the example bot: + ```bash + python examples/basic.py + ``` + +### Contributing + +1. Fork the repository +2. Create a feature branch +3. Make your changes +4. Run linter, formatter and type checker: `ruff check .`,`ruff format .`, + `basedpyright .` +5. Submit a pull request + +**Development Tools**: + +- **uv**: For dependency management +- **Ruff**: For linting and formatting +- **HashiCorp Copywrite**: For managing license headers +- **basedpyright**: For type checking + + +> [!CAUTION] +> This is an early-stage project and may have unexpected behaviors or bugs. Please report any issues you encounter. + +## License + +MIT License - Copyright (c) 2025 Paillat-dev + +--- + +Made with ❤ by Paillat-dev diff --git a/examples/basic.py b/examples/basic.py new file mode 100644 index 0000000..8fef802 --- /dev/null +++ b/examples/basic.py @@ -0,0 +1,40 @@ +# Copyright (c) Paillat-dev +# SPDX-License-Identifier: MIT + +import os + +import discord +from dotenv import load_dotenv + +from discord_progress_bar import ProgressBar, ProgressBarManager + +# Load environment variables from .env file +load_dotenv() + +bot = discord.Bot(cache_app_emojis=True) + + +class MyCog(discord.Cog): + def __init__(self, bot: discord.Bot) -> None: + self.bot: discord.Bot = bot + self.progress_bar_manager: ProgressBarManager + self.progress_bar: ProgressBar + + @discord.Cog.listener() + async def on_ready(self) -> None: + print(f"Logged in as {bot.user} (ID: {bot.user.id})") + print("------") + self.progress_bar_manager = await ProgressBarManager(self.bot) + self.progress_bar = await self.progress_bar_manager.progress_bar("green", length=10) + print("Progress bar manager loaded.") + + @discord.slash_command() + async def get_progress_bar(self, ctx: discord.ApplicationContext, percent: float | None = None) -> None: + """Send a progress bar message.""" + if percent is None: + percent = 1 + await ctx.respond(f"Progress: {self.progress_bar.partial(percent)}") + + +bot.add_cog(MyCog(bot)) +bot.run(os.environ["DISCORD_TOKEN"]) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..4e0430a --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,124 @@ +[build-system] +requires = ["hatchling", "hatch-vcs"] +build-backend = "hatchling.build" + +[project] +name = "discord-progress-bar" +dynamic = ["version", "urls"] +description = "A python library to easily create progress bars with discord emojis" +readme = "README.md" +authors = [ + { name = "Paillat-dev", email = "me@paillat.dev" } +] +license = "MIT" +requires-python = "==3.12.*" +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.12", + "Typing :: Typed", + "Operating System :: OS Independent", +] +keywords = ["discord", "bot", "emojis", "emoji", "progress-bar"] +dependencies = [ + "aiofile>=3.9.0", + "aiohttp>=3.11.18", + "py-cord", +] + +[dependency-groups] +dev = [ + "basedpyright>=1.28.1", + "python-dotenv>=1.0.1", + "ruff>=0.9.9", +] + +[tool.hatch.version] +source = "vcs" + +[tool.hatch.build.hooks.vcs] +version-file = "src/discord_progress_bar/_version.py" + +[tool.hatch.metadata.hooks.vcs.urls] +Homepage = "https://github.com/Paillat-dev/discord-progress-bar" +source_archive = "https://github.com/Paillat-dev/discord-progress-bar/archive/{commit_hash}.zip" + +[tool.hatchling] +name = "discord-progress-bar" + +[tool.hatch.build] +include = [ + "src/discord_progress_bar/", +] +exclude = [ + ".copywrite.hcl", + ".github", + ".python-version", + "uv.lock", +] + +[tool.hatch.build.targets.wheel] +packages = ["src/discord_progress_bar"] + +[tool.pyright] +pythonVersion = "3.12" +typeCheckingMode = "all" +reportUnusedCallResult = false +reportAny = false +executionEnvironments = [ + { root = "src/discord_progress_bar/_version.py", reportDeprecated = false }, + { root = "examples", reportExplicitAny = false, reportUnknownMemberType = false, reportUnusedParameter = false, reportImplicitOverride = false } +] + +[tool.ruff] +target-version = "py312" +line-length = 120 +indent-width = 4 + +[tool.ruff.format] +quote-style = "double" +indent-style = "space" +skip-magic-trailing-comma = false +line-ending = "auto" +docstring-code-format = false +docstring-code-line-length = "dynamic" +exclude = [ + "src/discord_progress_bar/_version.py" +] + +[tool.ruff.lint] +select = ["ALL"] +per-file-ignores = { "examples/**/*" = ["INP001", "ARG002", "T201"] } +extend-ignore = [ + "N999", + "D104", + "D100", + "D103", + "D102", + "D101", + "D107", + "D105", + "D106", + "ANN401", + "TRY003", + "EM101", + "EM102", + "G004", + "PTH", + "D211", + "D213", + "COM812", + "ISC001", + "D203", + "FBT001", + "FBT002", + "PLR2004", + "PLR0913", + "C901", + "ISC003" # conflicts with basedpyright reportImplicitStringConcatenation +] + +[tool.uv.sources] +py-cord = { git = "https://github.com/Pycord-Development/pycord" } diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..53d3662 --- /dev/null +++ b/renovate.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": ["config:recommended"], + "baseBranches": ["main"], + "labels": ["deps"], + "ignorePaths": ["requirements.txt"], + "commitMessagePrefix": "⬆️", + "commitMessageAction": "Upgrade", + "packageRules": [ + { + "updateTypes": ["pin"], + "commitMessagePrefix": "📌", + "commitMessageAction": "Pin" + }, + { + "updateTypes": ["rollback"], + "commitMessagePrefix": "⬇️", + "commitMessageAction": "Downgrade" + }, + { + "matchDatasources": ["pypi"], + "addLabels": ["pypi"] + } + ] +} diff --git a/src/discord_progress_bar/__init__.py b/src/discord_progress_bar/__init__.py new file mode 100644 index 0000000..db9eae2 --- /dev/null +++ b/src/discord_progress_bar/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) Paillat-dev +# SPDX-License-Identifier: MIT +from .progress_bar import ProgressBar, ProgressBarManager +from .types import ProgressBarEmojiMapping, ProgressBarPart, ProgressBarPathMapping, ProgressBarUrlMapping + +__all__ = ( + "ProgressBar", + "ProgressBarEmojiMapping", + "ProgressBarManager", + "ProgressBarPart", + "ProgressBarPathMapping", + "ProgressBarUrlMapping", +) diff --git a/src/discord_progress_bar/default_bars.py b/src/discord_progress_bar/default_bars.py new file mode 100644 index 0000000..287854f --- /dev/null +++ b/src/discord_progress_bar/default_bars.py @@ -0,0 +1,16 @@ +# Copyright (c) Paillat-dev +# SPDX-License-Identifier: MIT + +from .types import ProgressBarPart, ProgressBarUrlMapping + +DEFAULT_PROGRESS_BARS: dict[str, ProgressBarUrlMapping] = { + "green": { + # From https://emoji.gg/user/ravenastar + ProgressBarPart.LEFT_EMPTY: "https://cdn3.emoji.gg/emojis/5499-lb2-g.png", + ProgressBarPart.LEFT_FILLED: "https://cdn3.emoji.gg/emojis/5988-lb-g.png", + ProgressBarPart.MIDDLE_EMPTY: "https://cdn3.emoji.gg/emojis/2827-l2-g.png", + ProgressBarPart.MIDDLE_FILLED: "https://cdn3.emoji.gg/emojis/3451-l-g.png", + ProgressBarPart.RIGHT_EMPTY: "https://cdn3.emoji.gg/emojis/2881-lb3-g.png", + ProgressBarPart.RIGHT_FILLED: "https://cdn3.emoji.gg/emojis/3166-lb4-g.png", + } +} diff --git a/src/discord_progress_bar/progress_bar.py b/src/discord_progress_bar/progress_bar.py new file mode 100644 index 0000000..96a6b48 --- /dev/null +++ b/src/discord_progress_bar/progress_bar.py @@ -0,0 +1,115 @@ +# Copyright (c) Paillat-dev +# SPDX-License-Identifier: MIT + +from collections import defaultdict +from typing import Self, cast + +import aiofile +import aiohttp +import discord + +from .default_bars import DEFAULT_PROGRESS_BARS +from .types import ProgressBarEmojiMapping, ProgressBarPart, ProgressBarPathMapping, ProgressBarUrlMapping + + +class ProgressBar: + def __init__(self, mapping: ProgressBarEmojiMapping, length: int = 5) -> None: + if length < 2: + raise ValueError("Length must be at least 2.") + self._mapping: ProgressBarEmojiMapping = mapping + self._length: int = length + + def full(self) -> str: + return "".join( + [ + str(self._mapping[ProgressBarPart.LEFT_FILLED]) + + str(self._mapping[ProgressBarPart.MIDDLE_FILLED]) * (self._length - 2) + + str(self._mapping[ProgressBarPart.RIGHT_FILLED]) + ] + ) + + def empty(self) -> str: + return "".join( + [ + str(self._mapping[ProgressBarPart.LEFT_EMPTY]) + + str(self._mapping[ProgressBarPart.MIDDLE_EMPTY]) * (self._length - 2) + + str(self._mapping[ProgressBarPart.RIGHT_EMPTY]) + ] + ) + + def partial(self, percent: float) -> str: + if percent < 0 or percent > 1: + raise ValueError("Percent must be between 0 and 1.") + filled_length = round(self._length * percent) + empty_length = self._length - filled_length + + # Handle edge cases + if filled_length == 0: + return self.empty() + if filled_length == self._length: + return self.full() + + # For partial progress, ensure we have the correct number of middle emojis + middle_filled_count = max(0, filled_length - 1) # At least one for left filled + middle_empty_count = max(0, empty_length - 1) # At least one for right empty + + return "".join( + [ + str(self._mapping[ProgressBarPart.LEFT_FILLED]) + + str(self._mapping[ProgressBarPart.MIDDLE_FILLED]) * middle_filled_count + + str(self._mapping[ProgressBarPart.MIDDLE_EMPTY]) * middle_empty_count + + str(self._mapping[ProgressBarPart.RIGHT_EMPTY]) + ] + ) + + +class ProgressBarManager: + def __init__(self, bot: discord.Bot) -> None: + self._bot: discord.Bot = bot + self._emojis: dict[str, ProgressBarEmojiMapping] = defaultdict(dict) + + async def __new__(cls, bot: discord.Bot) -> Self: + obj = super().__new__(cls) + obj.__init__(bot) + await obj.load() + return obj + + async def load(self) -> None: + await self._bot.wait_until_ready() + for emoji in self._bot.app_emojis: + if emoji.name.startswith("pb_"): + key: str = emoji.name.split("_")[1] + part: ProgressBarPart = cast("ProgressBarPart", int(emoji.name.split("_")[2])) + self._emojis[key][part] = emoji + + async def create_emojis_from_urls(self, name: str, emojis: ProgressBarUrlMapping) -> None: + async with aiohttp.ClientSession() as session: + for key, url in emojis.items(): + emoji_name = f"pb_{name}_{key}" + response = await session.get(url) + response.raise_for_status() + image = await response.read() + emoji = await self._bot.create_emoji( + name=emoji_name, + image=image, + ) + self._emojis[name][key] = emoji + + async def create_emojis_from_files(self, name: str, emojis: ProgressBarPathMapping) -> None: + for key, path in emojis.items(): + emoji_name = f"pb_{name}_{key}" + async with aiofile.async_open(path, "rb") as f: + image = await f.read() + emoji = await self._bot.create_emoji( + name=emoji_name, + image=image, + ) + self._emojis[name][key] = emoji + + async def progress_bar(self, name: str, *, length: int = 5) -> ProgressBar: + if name not in self._emojis: + if default := DEFAULT_PROGRESS_BARS.get(name): + await self.create_emojis_from_urls(name, default) + else: + raise ValueError(f"Progress bar {name} not found.") + return ProgressBar(self._emojis[name], length=length) diff --git a/src/discord_progress_bar/py.typed b/src/discord_progress_bar/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/src/discord_progress_bar/types.py b/src/discord_progress_bar/types.py new file mode 100644 index 0000000..d82cb31 --- /dev/null +++ b/src/discord_progress_bar/types.py @@ -0,0 +1,27 @@ +# ruff: noqa: A005 +# Copyright (c) Paillat-dev +# SPDX-License-Identifier: MIT + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + import pathlib +from enum import IntEnum + +import discord + + +class ProgressBarPart(IntEnum): + """Enum for the different parts of a progress bar.""" + + LEFT_EMPTY = 0 + LEFT_FILLED = 1 + MIDDLE_EMPTY = 2 + MIDDLE_FILLED = 3 + RIGHT_EMPTY = 4 + RIGHT_FILLED = 5 + + +type ProgressBarUrlMapping = dict[ProgressBarPart, str] +type ProgressBarPathMapping = dict[ProgressBarPart, pathlib.Path] +type ProgressBarEmojiMapping = dict[ProgressBarPart, discord.AppEmoji] diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..695a6bd --- /dev/null +++ b/uv.lock @@ -0,0 +1,316 @@ +version = 1 +revision = 1 +requires-python = "==3.12.*" + +[[package]] +name = "aiofile" +version = "3.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "caio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/67/e2/d7cb819de8df6b5c1968a2756c3cb4122d4fa2b8fc768b53b7c9e5edb646/aiofile-3.9.0.tar.gz", hash = "sha256:e5ad718bb148b265b6df1b3752c4d1d83024b93da9bd599df74b9d9ffcf7919b", size = 17943 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/25/da1f0b4dd970e52bf5a36c204c107e11a0c6d3ed195eba0bfbc664c312b2/aiofile-3.9.0-py3-none-any.whl", hash = "sha256:ce2f6c1571538cbdfa0143b04e16b208ecb0e9cb4148e528af8a640ed51cc8aa", size = 19539 }, +] + +[[package]] +name = "aiohappyeyeballs" +version = "2.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265 }, +] + +[[package]] +name = "aiohttp" +version = "3.11.18" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohappyeyeballs" }, + { name = "aiosignal" }, + { name = "attrs" }, + { name = "frozenlist" }, + { name = "multidict" }, + { name = "propcache" }, + { name = "yarl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/e7/fa1a8c00e2c54b05dc8cb5d1439f627f7c267874e3f7bb047146116020f9/aiohttp-3.11.18.tar.gz", hash = "sha256:ae856e1138612b7e412db63b7708735cff4d38d0399f6a5435d3dac2669f558a", size = 7678653 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/d2/5bc436f42bf4745c55f33e1e6a2d69e77075d3e768e3d1a34f96ee5298aa/aiohttp-3.11.18-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:63d71eceb9cad35d47d71f78edac41fcd01ff10cacaa64e473d1aec13fa02df2", size = 706671 }, + { url = "https://files.pythonhosted.org/packages/fe/d0/2dbabecc4e078c0474abb40536bbde717fb2e39962f41c5fc7a216b18ea7/aiohttp-3.11.18-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d1929da615840969929e8878d7951b31afe0bac883d84418f92e5755d7b49508", size = 466169 }, + { url = "https://files.pythonhosted.org/packages/70/84/19edcf0b22933932faa6e0be0d933a27bd173da02dc125b7354dff4d8da4/aiohttp-3.11.18-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d0aebeb2392f19b184e3fdd9e651b0e39cd0f195cdb93328bd124a1d455cd0e", size = 457554 }, + { url = "https://files.pythonhosted.org/packages/32/d0/e8d1f034ae5624a0f21e4fb3feff79342ce631f3a4d26bd3e58b31ef033b/aiohttp-3.11.18-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3849ead845e8444f7331c284132ab314b4dac43bfae1e3cf350906d4fff4620f", size = 1690154 }, + { url = "https://files.pythonhosted.org/packages/16/de/2f9dbe2ac6f38f8495562077131888e0d2897e3798a0ff3adda766b04a34/aiohttp-3.11.18-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5e8452ad6b2863709f8b3d615955aa0807bc093c34b8e25b3b52097fe421cb7f", size = 1733402 }, + { url = "https://files.pythonhosted.org/packages/e0/04/bd2870e1e9aef990d14b6df2a695f17807baf5c85a4c187a492bda569571/aiohttp-3.11.18-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b8d2b42073611c860a37f718b3d61ae8b4c2b124b2e776e2c10619d920350ec", size = 1783958 }, + { url = "https://files.pythonhosted.org/packages/23/06/4203ffa2beb5bedb07f0da0f79b7d9039d1c33f522e0d1a2d5b6218e6f2e/aiohttp-3.11.18-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40fbf91f6a0ac317c0a07eb328a1384941872f6761f2e6f7208b63c4cc0a7ff6", size = 1695288 }, + { url = "https://files.pythonhosted.org/packages/30/b2/e2285dda065d9f29ab4b23d8bcc81eb881db512afb38a3f5247b191be36c/aiohttp-3.11.18-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ff5625413fec55216da5eaa011cf6b0a2ed67a565914a212a51aa3755b0009", size = 1618871 }, + { url = "https://files.pythonhosted.org/packages/57/e0/88f2987885d4b646de2036f7296ebea9268fdbf27476da551c1a7c158bc0/aiohttp-3.11.18-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7f33a92a2fde08e8c6b0c61815521324fc1612f397abf96eed86b8e31618fdb4", size = 1646262 }, + { url = "https://files.pythonhosted.org/packages/e0/19/4d2da508b4c587e7472a032290b2981f7caeca82b4354e19ab3df2f51d56/aiohttp-3.11.18-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:11d5391946605f445ddafda5eab11caf310f90cdda1fd99865564e3164f5cff9", size = 1677431 }, + { url = "https://files.pythonhosted.org/packages/eb/ae/047473ea50150a41440f3265f53db1738870b5a1e5406ece561ca61a3bf4/aiohttp-3.11.18-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3cc314245deb311364884e44242e00c18b5896e4fe6d5f942e7ad7e4cb640adb", size = 1637430 }, + { url = "https://files.pythonhosted.org/packages/11/32/c6d1e3748077ce7ee13745fae33e5cb1dac3e3b8f8787bf738a93c94a7d2/aiohttp-3.11.18-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0f421843b0f70740772228b9e8093289924359d306530bcd3926f39acbe1adda", size = 1703342 }, + { url = "https://files.pythonhosted.org/packages/c5/1d/a3b57bfdbe285f0d45572d6d8f534fd58761da3e9cbc3098372565005606/aiohttp-3.11.18-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e220e7562467dc8d589e31c1acd13438d82c03d7f385c9cd41a3f6d1d15807c1", size = 1740600 }, + { url = "https://files.pythonhosted.org/packages/a5/71/f9cd2fed33fa2b7ce4d412fb7876547abb821d5b5520787d159d0748321d/aiohttp-3.11.18-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ab2ef72f8605046115bc9aa8e9d14fd49086d405855f40b79ed9e5c1f9f4faea", size = 1695131 }, + { url = "https://files.pythonhosted.org/packages/97/97/d1248cd6d02b9de6aa514793d0dcb20099f0ec47ae71a933290116c070c5/aiohttp-3.11.18-cp312-cp312-win32.whl", hash = "sha256:12a62691eb5aac58d65200c7ae94d73e8a65c331c3a86a2e9670927e94339ee8", size = 412442 }, + { url = "https://files.pythonhosted.org/packages/33/9a/e34e65506e06427b111e19218a99abf627638a9703f4b8bcc3e3021277ed/aiohttp-3.11.18-cp312-cp312-win_amd64.whl", hash = "sha256:364329f319c499128fd5cd2d1c31c44f234c58f9b96cc57f743d16ec4f3238c8", size = 439444 }, +] + +[[package]] +name = "aiosignal" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "frozenlist" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ba/b5/6d55e80f6d8a08ce22b982eafa278d823b541c925f11ee774b0b9c43473d/aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54", size = 19424 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/6a/bc7e17a3e87a2985d3e8f4da4cd0f481060eb78fb08596c42be62c90a4d9/aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5", size = 7597 }, +] + +[[package]] +name = "attrs" +version = "25.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815 }, +] + +[[package]] +name = "basedpyright" +version = "1.29.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nodejs-wheel-binaries" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/18/f5e488eac4960ad9a2e71b95f0d91cf93a982c7f68aa90e4e0554f0bc37e/basedpyright-1.29.1.tar.gz", hash = "sha256:06bbe6c3b50ab4af20f80e154049477a50d8b81d2522eadbc9f472f2f92cd44b", size = 21773469 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/1b/1bb837bbb7e259928f33d3c105dfef4f5349ef08b3ef45576801256e3234/basedpyright-1.29.1-py3-none-any.whl", hash = "sha256:b7eb65b9d4aaeeea29a349ac494252032a75a364942d0ac466d7f07ddeacc786", size = 11397959 }, +] + +[[package]] +name = "caio" +version = "0.9.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b7/cf/5a65eea5f6c01c4df4f5bd10fab2a47ec31fda44a6c7cb94a17392feb428/caio-0.9.22.tar.gz", hash = "sha256:7ea533d90e5fa0bba33bc8f4805b4c90f19e8d8ac5139a8033b92a6ab5c4012b", size = 26750 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/15/1d898c54a815ec06b33a8259e8b68c955e098d42a4a16063b23d1f5c9f5c/caio-0.9.22-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:25aae4c3b846eeca63c7d75d63b3fd8deaba9650ef1168d8a239e6a927299ded", size = 42372 }, + { url = "https://files.pythonhosted.org/packages/f5/be/a839d1053a8aeb6d9641b0759d6ab9defc832bf5681318e51a8d6559eb74/caio-0.9.22-cp312-cp312-manylinux_2_34_aarch64.whl", hash = "sha256:ac332b8e2c8e0840fe10ee6971b38f8dac6ad64ecf6087ee3f70cd376f511699", size = 81558 }, + { url = "https://files.pythonhosted.org/packages/cd/4f/1736f6b1e0df00d864ef5010293a4da3309bd02faa98ea35f339b817d5dc/caio-0.9.22-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:6f991e4812fd2d6e87f91ff78fcc7d4f299bd98765334756580d4ea42cad89f1", size = 80254 }, +] + +[[package]] +name = "discord-progress-bar" +source = { editable = "." } +dependencies = [ + { name = "aiofile" }, + { name = "aiohttp" }, + { name = "py-cord" }, +] + +[package.dev-dependencies] +dev = [ + { name = "basedpyright" }, + { name = "python-dotenv" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "aiofile", specifier = ">=3.9.0" }, + { name = "aiohttp", specifier = ">=3.11.18" }, + { name = "py-cord", git = "https://github.com/Pycord-Development/pycord" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "basedpyright", specifier = ">=1.28.1" }, + { name = "python-dotenv", specifier = ">=1.0.1" }, + { name = "ruff", specifier = ">=0.9.9" }, +] + +[[package]] +name = "frozenlist" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/f4/d744cba2da59b5c1d88823cf9e8a6c74e4659e2b27604ed973be2a0bf5ab/frozenlist-1.6.0.tar.gz", hash = "sha256:b99655c32c1c8e06d111e7f41c06c29a5318cb1835df23a45518e02a47c63b68", size = 42831 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9c/8a/289b7d0de2fbac832ea80944d809759976f661557a38bb8e77db5d9f79b7/frozenlist-1.6.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c5b9e42ace7d95bf41e19b87cec8f262c41d3510d8ad7514ab3862ea2197bfb1", size = 160193 }, + { url = "https://files.pythonhosted.org/packages/19/80/2fd17d322aec7f430549f0669f599997174f93ee17929ea5b92781ec902c/frozenlist-1.6.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ca9973735ce9f770d24d5484dcb42f68f135351c2fc81a7a9369e48cf2998a29", size = 123831 }, + { url = "https://files.pythonhosted.org/packages/99/06/f5812da431273f78c6543e0b2f7de67dfd65eb0a433978b2c9c63d2205e4/frozenlist-1.6.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6ac40ec76041c67b928ca8aaffba15c2b2ee3f5ae8d0cb0617b5e63ec119ca25", size = 121862 }, + { url = "https://files.pythonhosted.org/packages/d0/31/9e61c6b5fc493cf24d54881731204d27105234d09878be1a5983182cc4a5/frozenlist-1.6.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95b7a8a3180dfb280eb044fdec562f9b461614c0ef21669aea6f1d3dac6ee576", size = 316361 }, + { url = "https://files.pythonhosted.org/packages/9d/55/22ca9362d4f0222324981470fd50192be200154d51509ee6eb9baa148e96/frozenlist-1.6.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c444d824e22da6c9291886d80c7d00c444981a72686e2b59d38b285617cb52c8", size = 307115 }, + { url = "https://files.pythonhosted.org/packages/ae/39/4fff42920a57794881e7bb3898dc7f5f539261711ea411b43bba3cde8b79/frozenlist-1.6.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb52c8166499a8150bfd38478248572c924c003cbb45fe3bcd348e5ac7c000f9", size = 322505 }, + { url = "https://files.pythonhosted.org/packages/55/f2/88c41f374c1e4cf0092a5459e5f3d6a1e17ed274c98087a76487783df90c/frozenlist-1.6.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b35298b2db9c2468106278537ee529719228950a5fdda686582f68f247d1dc6e", size = 322666 }, + { url = "https://files.pythonhosted.org/packages/75/51/034eeb75afdf3fd03997856195b500722c0b1a50716664cde64e28299c4b/frozenlist-1.6.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d108e2d070034f9d57210f22fefd22ea0d04609fc97c5f7f5a686b3471028590", size = 302119 }, + { url = "https://files.pythonhosted.org/packages/2b/a6/564ecde55ee633270a793999ef4fd1d2c2b32b5a7eec903b1012cb7c5143/frozenlist-1.6.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e1be9111cb6756868ac242b3c2bd1f09d9aea09846e4f5c23715e7afb647103", size = 316226 }, + { url = "https://files.pythonhosted.org/packages/f1/c8/6c0682c32377f402b8a6174fb16378b683cf6379ab4d2827c580892ab3c7/frozenlist-1.6.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:94bb451c664415f02f07eef4ece976a2c65dcbab9c2f1705b7031a3a75349d8c", size = 312788 }, + { url = "https://files.pythonhosted.org/packages/b6/b8/10fbec38f82c5d163ca1750bfff4ede69713badf236a016781cf1f10a0f0/frozenlist-1.6.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:d1a686d0b0949182b8faddea596f3fc11f44768d1f74d4cad70213b2e139d821", size = 325914 }, + { url = "https://files.pythonhosted.org/packages/62/ca/2bf4f3a1bd40cdedd301e6ecfdbb291080d5afc5f9ce350c0739f773d6b9/frozenlist-1.6.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:ea8e59105d802c5a38bdbe7362822c522230b3faba2aa35c0fa1765239b7dd70", size = 305283 }, + { url = "https://files.pythonhosted.org/packages/09/64/20cc13ccf94abc2a1f482f74ad210703dc78a590d0b805af1c9aa67f76f9/frozenlist-1.6.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:abc4e880a9b920bc5020bf6a431a6bb40589d9bca3975c980495f63632e8382f", size = 319264 }, + { url = "https://files.pythonhosted.org/packages/20/ff/86c6a2bbe98cfc231519f5e6d712a0898488ceac804a917ce014f32e68f6/frozenlist-1.6.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9a79713adfe28830f27a3c62f6b5406c37376c892b05ae070906f07ae4487046", size = 326482 }, + { url = "https://files.pythonhosted.org/packages/2f/da/8e381f66367d79adca245d1d71527aac774e30e291d41ef161ce2d80c38e/frozenlist-1.6.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9a0318c2068e217a8f5e3b85e35899f5a19e97141a45bb925bb357cfe1daf770", size = 318248 }, + { url = "https://files.pythonhosted.org/packages/39/24/1a1976563fb476ab6f0fa9fefaac7616a4361dbe0461324f9fd7bf425dbe/frozenlist-1.6.0-cp312-cp312-win32.whl", hash = "sha256:853ac025092a24bb3bf09ae87f9127de9fe6e0c345614ac92536577cf956dfcc", size = 115161 }, + { url = "https://files.pythonhosted.org/packages/80/2e/fb4ed62a65f8cd66044706b1013f0010930d8cbb0729a2219561ea075434/frozenlist-1.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:2bdfe2d7e6c9281c6e55523acd6c2bf77963cb422fdc7d142fb0cb6621b66878", size = 120548 }, + { url = "https://files.pythonhosted.org/packages/71/3e/b04a0adda73bd52b390d730071c0d577073d3d26740ee1bad25c3ad0f37b/frozenlist-1.6.0-py3-none-any.whl", hash = "sha256:535eec9987adb04701266b92745d6cdcef2e77669299359c3009c3404dd5d191", size = 12404 }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, +] + +[[package]] +name = "multidict" +version = "6.4.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/da/2c/e367dfb4c6538614a0c9453e510d75d66099edf1c4e69da1b5ce691a1931/multidict-6.4.3.tar.gz", hash = "sha256:3ada0b058c9f213c5f95ba301f922d402ac234f1111a7d8fd70f1b99f3c281ec", size = 89372 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/bb/3abdaf8fe40e9226ce8a2ba5ecf332461f7beec478a455d6587159f1bf92/multidict-6.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1f1c2f58f08b36f8475f3ec6f5aeb95270921d418bf18f90dffd6be5c7b0e676", size = 64019 }, + { url = "https://files.pythonhosted.org/packages/7e/b5/1b2e8de8217d2e89db156625aa0fe4a6faad98972bfe07a7b8c10ef5dd6b/multidict-6.4.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:26ae9ad364fc61b936fb7bf4c9d8bd53f3a5b4417142cd0be5c509d6f767e2f1", size = 37925 }, + { url = "https://files.pythonhosted.org/packages/b4/e2/3ca91c112644a395c8eae017144c907d173ea910c913ff8b62549dcf0bbf/multidict-6.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:659318c6c8a85f6ecfc06b4e57529e5a78dfdd697260cc81f683492ad7e9435a", size = 37008 }, + { url = "https://files.pythonhosted.org/packages/60/23/79bc78146c7ac8d1ac766b2770ca2e07c2816058b8a3d5da6caed8148637/multidict-6.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1eb72c741fd24d5a28242ce72bb61bc91f8451877131fa3fe930edb195f7054", size = 224374 }, + { url = "https://files.pythonhosted.org/packages/86/35/77950ed9ebd09136003a85c1926ba42001ca5be14feb49710e4334ee199b/multidict-6.4.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3cd06d88cb7398252284ee75c8db8e680aa0d321451132d0dba12bc995f0adcc", size = 230869 }, + { url = "https://files.pythonhosted.org/packages/49/97/2a33c6e7d90bc116c636c14b2abab93d6521c0c052d24bfcc231cbf7f0e7/multidict-6.4.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4543d8dc6470a82fde92b035a92529317191ce993533c3c0c68f56811164ed07", size = 231949 }, + { url = "https://files.pythonhosted.org/packages/56/ce/e9b5d9fcf854f61d6686ada7ff64893a7a5523b2a07da6f1265eaaea5151/multidict-6.4.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:30a3ebdc068c27e9d6081fca0e2c33fdf132ecea703a72ea216b81a66860adde", size = 231032 }, + { url = "https://files.pythonhosted.org/packages/f0/ac/7ced59dcdfeddd03e601edb05adff0c66d81ed4a5160c443e44f2379eef0/multidict-6.4.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b038f10e23f277153f86f95c777ba1958bcd5993194fda26a1d06fae98b2f00c", size = 223517 }, + { url = "https://files.pythonhosted.org/packages/db/e6/325ed9055ae4e085315193a1b58bdb4d7fc38ffcc1f4975cfca97d015e17/multidict-6.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c605a2b2dc14282b580454b9b5d14ebe0668381a3a26d0ac39daa0ca115eb2ae", size = 216291 }, + { url = "https://files.pythonhosted.org/packages/fa/84/eeee6d477dd9dcb7691c3bb9d08df56017f5dd15c730bcc9383dcf201cf4/multidict-6.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8bd2b875f4ca2bb527fe23e318ddd509b7df163407b0fb717df229041c6df5d3", size = 228982 }, + { url = "https://files.pythonhosted.org/packages/82/94/4d1f3e74e7acf8b0c85db350e012dcc61701cd6668bc2440bb1ecb423c90/multidict-6.4.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c2e98c840c9c8e65c0e04b40c6c5066c8632678cd50c8721fdbcd2e09f21a507", size = 226823 }, + { url = "https://files.pythonhosted.org/packages/09/f0/1e54b95bda7cd01080e5732f9abb7b76ab5cc795b66605877caeb2197476/multidict-6.4.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:66eb80dd0ab36dbd559635e62fba3083a48a252633164857a1d1684f14326427", size = 222714 }, + { url = "https://files.pythonhosted.org/packages/e7/a2/f6cbca875195bd65a3e53b37ab46486f3cc125bdeab20eefe5042afa31fb/multidict-6.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c23831bdee0a2a3cf21be057b5e5326292f60472fb6c6f86392bbf0de70ba731", size = 233739 }, + { url = "https://files.pythonhosted.org/packages/79/68/9891f4d2b8569554723ddd6154375295f789dc65809826c6fb96a06314fd/multidict-6.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1535cec6443bfd80d028052e9d17ba6ff8a5a3534c51d285ba56c18af97e9713", size = 230809 }, + { url = "https://files.pythonhosted.org/packages/e6/72/a7be29ba1e87e4fc5ceb44dabc7940b8005fd2436a332a23547709315f70/multidict-6.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3b73e7227681f85d19dec46e5b881827cd354aabe46049e1a61d2f9aaa4e285a", size = 226934 }, + { url = "https://files.pythonhosted.org/packages/12/c1/259386a9ad6840ff7afc686da96808b503d152ac4feb3a96c651dc4f5abf/multidict-6.4.3-cp312-cp312-win32.whl", hash = "sha256:8eac0c49df91b88bf91f818e0a24c1c46f3622978e2c27035bfdca98e0e18124", size = 35242 }, + { url = "https://files.pythonhosted.org/packages/06/24/c8fdff4f924d37225dc0c56a28b1dca10728fc2233065fafeb27b4b125be/multidict-6.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:11990b5c757d956cd1db7cb140be50a63216af32cd6506329c2c59d732d802db", size = 38635 }, + { url = "https://files.pythonhosted.org/packages/96/10/7d526c8974f017f1e7ca584c71ee62a638e9334d8d33f27d7cdfc9ae79e4/multidict-6.4.3-py3-none-any.whl", hash = "sha256:59fe01ee8e2a1e8ceb3f6dbb216b09c8d9f4ef1c22c4fc825d045a147fa2ebc9", size = 10400 }, +] + +[[package]] +name = "nodejs-wheel-binaries" +version = "22.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/c7/4fd3871d2b7fd5122216245e273201ab98eda92bbd6fe9ad04846b758c56/nodejs_wheel_binaries-22.14.0.tar.gz", hash = "sha256:c1dc43713598c7310d53795c764beead861b8c5021fe4b1366cb912ce1a4c8bf", size = 8055 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/61/b6/66ef4ef75ea7389ea788f2d5505bf9a8e5c3806d56c7a90cf46a6942f1cf/nodejs_wheel_binaries-22.14.0-py2.py3-none-macosx_11_0_arm64.whl", hash = "sha256:d8ab8690516a3e98458041286e3f0d6458de176d15c14f205c3ea2972131420d", size = 50326597 }, + { url = "https://files.pythonhosted.org/packages/7d/78/023d91a293ba73572a643bc89d11620d189f35f205a309dd8296aa45e69a/nodejs_wheel_binaries-22.14.0-py2.py3-none-macosx_11_0_x86_64.whl", hash = "sha256:b2f200f23b3610bdbee01cf136279e005ffdf8ee74557aa46c0940a7867956f6", size = 51158258 }, + { url = "https://files.pythonhosted.org/packages/af/86/324f6342c79e5034a13319b02ba9ed1f4ac8813af567d223c9a9e56cd338/nodejs_wheel_binaries-22.14.0-py2.py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0877832abd7a9c75c8c5caafa37f986c9341ee025043c2771213d70c4c1defa", size = 57180264 }, + { url = "https://files.pythonhosted.org/packages/6d/9f/42bdaab26137e31732bff00147b9aca2185d475b5752b57a443e6c7ba93f/nodejs_wheel_binaries-22.14.0-py2.py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8fded5a70a8a55c2135e67bd580d8b7f2e94fcbafcc679b6a2d5b92f88373d69", size = 57693251 }, + { url = "https://files.pythonhosted.org/packages/ab/d7/94f8f269aa86cf35f9ed2b70d09aca48dc971fb5656fdc4a3b69364b189f/nodejs_wheel_binaries-22.14.0-py2.py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c1ade6f3ece458b40c02e89c91d5103792a9f18aaad5026da533eb0dcb87090e", size = 58841717 }, + { url = "https://files.pythonhosted.org/packages/2d/a0/43b7316eaf22b4ee9bfb897ee36c724efceac7b89d7d1bedca28057b7be1/nodejs_wheel_binaries-22.14.0-py2.py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:34fa5ed4cf3f65cbfbe9b45c407ffc2fc7d97a06cd8993e6162191ff81f29f48", size = 59808791 }, + { url = "https://files.pythonhosted.org/packages/10/0a/814491f751a25136e37de68a2728c9a9e3c1d20494aba5ff3c230d5f9c2d/nodejs_wheel_binaries-22.14.0-py2.py3-none-win_amd64.whl", hash = "sha256:ca7023276327455988b81390fa6bbfa5191c1da7fc45bc57c7abc281ba9967e9", size = 40478921 }, + { url = "https://files.pythonhosted.org/packages/f4/5c/cab444afaa387dceac8debb817b52fd00596efcd2d54506c27311c6fe6a8/nodejs_wheel_binaries-22.14.0-py2.py3-none-win_arm64.whl", hash = "sha256:fd59c8e9a202221e316febe1624a1ae3b42775b7fb27737bf12ec79565983eaf", size = 36206637 }, +] + +[[package]] +name = "propcache" +version = "0.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/07/c8/fdc6686a986feae3541ea23dcaa661bd93972d3940460646c6bb96e21c40/propcache-0.3.1.tar.gz", hash = "sha256:40d980c33765359098837527e18eddefc9a24cea5b45e078a7f3bb5b032c6ecf", size = 43651 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/aa/ca78d9be314d1e15ff517b992bebbed3bdfef5b8919e85bf4940e57b6137/propcache-0.3.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f78eb8422acc93d7b69964012ad7048764bb45a54ba7a39bb9e146c72ea29723", size = 80430 }, + { url = "https://files.pythonhosted.org/packages/1a/d8/f0c17c44d1cda0ad1979af2e593ea290defdde9eaeb89b08abbe02a5e8e1/propcache-0.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:89498dd49c2f9a026ee057965cdf8192e5ae070ce7d7a7bd4b66a8e257d0c976", size = 46637 }, + { url = "https://files.pythonhosted.org/packages/ae/bd/c1e37265910752e6e5e8a4c1605d0129e5b7933c3dc3cf1b9b48ed83b364/propcache-0.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:09400e98545c998d57d10035ff623266927cb784d13dd2b31fd33b8a5316b85b", size = 46123 }, + { url = "https://files.pythonhosted.org/packages/d4/b0/911eda0865f90c0c7e9f0415d40a5bf681204da5fd7ca089361a64c16b28/propcache-0.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa8efd8c5adc5a2c9d3b952815ff8f7710cefdcaf5f2c36d26aff51aeca2f12f", size = 243031 }, + { url = "https://files.pythonhosted.org/packages/0a/06/0da53397c76a74271621807265b6eb61fb011451b1ddebf43213df763669/propcache-0.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c2fe5c910f6007e716a06d269608d307b4f36e7babee5f36533722660e8c4a70", size = 249100 }, + { url = "https://files.pythonhosted.org/packages/f1/eb/13090e05bf6b963fc1653cdc922133ced467cb4b8dab53158db5a37aa21e/propcache-0.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a0ab8cf8cdd2194f8ff979a43ab43049b1df0b37aa64ab7eca04ac14429baeb7", size = 250170 }, + { url = "https://files.pythonhosted.org/packages/3b/4c/f72c9e1022b3b043ec7dc475a0f405d4c3e10b9b1d378a7330fecf0652da/propcache-0.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:563f9d8c03ad645597b8d010ef4e9eab359faeb11a0a2ac9f7b4bc8c28ebef25", size = 245000 }, + { url = "https://files.pythonhosted.org/packages/e8/fd/970ca0e22acc829f1adf5de3724085e778c1ad8a75bec010049502cb3a86/propcache-0.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb6e0faf8cb6b4beea5d6ed7b5a578254c6d7df54c36ccd3d8b3eb00d6770277", size = 230262 }, + { url = "https://files.pythonhosted.org/packages/c4/42/817289120c6b9194a44f6c3e6b2c3277c5b70bbad39e7df648f177cc3634/propcache-0.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1c5c7ab7f2bb3f573d1cb921993006ba2d39e8621019dffb1c5bc94cdbae81e8", size = 236772 }, + { url = "https://files.pythonhosted.org/packages/7c/9c/3b3942b302badd589ad6b672da3ca7b660a6c2f505cafd058133ddc73918/propcache-0.3.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:050b571b2e96ec942898f8eb46ea4bfbb19bd5502424747e83badc2d4a99a44e", size = 231133 }, + { url = "https://files.pythonhosted.org/packages/98/a1/75f6355f9ad039108ff000dfc2e19962c8dea0430da9a1428e7975cf24b2/propcache-0.3.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e1c4d24b804b3a87e9350f79e2371a705a188d292fd310e663483af6ee6718ee", size = 230741 }, + { url = "https://files.pythonhosted.org/packages/67/0c/3e82563af77d1f8731132166da69fdfd95e71210e31f18edce08a1eb11ea/propcache-0.3.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e4fe2a6d5ce975c117a6bb1e8ccda772d1e7029c1cca1acd209f91d30fa72815", size = 244047 }, + { url = "https://files.pythonhosted.org/packages/f7/50/9fb7cca01532a08c4d5186d7bb2da6c4c587825c0ae134b89b47c7d62628/propcache-0.3.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:feccd282de1f6322f56f6845bf1207a537227812f0a9bf5571df52bb418d79d5", size = 246467 }, + { url = "https://files.pythonhosted.org/packages/a9/02/ccbcf3e1c604c16cc525309161d57412c23cf2351523aedbb280eb7c9094/propcache-0.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ec314cde7314d2dd0510c6787326bbffcbdc317ecee6b7401ce218b3099075a7", size = 241022 }, + { url = "https://files.pythonhosted.org/packages/db/19/e777227545e09ca1e77a6e21274ae9ec45de0f589f0ce3eca2a41f366220/propcache-0.3.1-cp312-cp312-win32.whl", hash = "sha256:7d2d5a0028d920738372630870e7d9644ce437142197f8c827194fca404bf03b", size = 40647 }, + { url = "https://files.pythonhosted.org/packages/24/bb/3b1b01da5dd04c77a204c84e538ff11f624e31431cfde7201d9110b092b1/propcache-0.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:88c423efef9d7a59dae0614eaed718449c09a5ac79a5f224a8b9664d603f04a3", size = 44784 }, + { url = "https://files.pythonhosted.org/packages/b8/d3/c3cb8f1d6ae3b37f83e1de806713a9b3642c5895f0215a62e1a4bd6e5e34/propcache-0.3.1-py3-none-any.whl", hash = "sha256:9a8ecf38de50a7f518c21568c80f985e776397b902f1ce0b01f799aba1608b40", size = 12376 }, +] + +[[package]] +name = "py-cord" +version = "2.6.1.dev233+gae38322c" +source = { git = "https://github.com/Pycord-Development/pycord#ae38322ca2bee850f3d05b0cbe80835a9e990986" } +dependencies = [ + { name = "aiohttp" }, + { name = "typing-extensions" }, +] + +[[package]] +name = "python-dotenv" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/88/2c/7bb1416c5620485aa793f2de31d3df393d3686aa8a8506d11e10e13c5baf/python_dotenv-1.1.0.tar.gz", hash = "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5", size = 39920 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/18/98a99ad95133c6a6e2005fe89faedf294a748bd5dc803008059409ac9b1e/python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d", size = 20256 }, +] + +[[package]] +name = "ruff" +version = "0.11.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d9/11/bcef6784c7e5d200b8a1f5c2ddf53e5da0efec37e6e5a44d163fb97e04ba/ruff-0.11.6.tar.gz", hash = "sha256:bec8bcc3ac228a45ccc811e45f7eb61b950dbf4cf31a67fa89352574b01c7d79", size = 4010053 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6e/1f/8848b625100ebcc8740c8bac5b5dd8ba97dd4ee210970e98832092c1635b/ruff-0.11.6-py3-none-linux_armv6l.whl", hash = "sha256:d84dcbe74cf9356d1bdb4a78cf74fd47c740bf7bdeb7529068f69b08272239a1", size = 10248105 }, + { url = "https://files.pythonhosted.org/packages/e0/47/c44036e70c6cc11e6ee24399c2a1e1f1e99be5152bd7dff0190e4b325b76/ruff-0.11.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9bc583628e1096148011a5d51ff3c836f51899e61112e03e5f2b1573a9b726de", size = 11001494 }, + { url = "https://files.pythonhosted.org/packages/ed/5b/170444061650202d84d316e8f112de02d092bff71fafe060d3542f5bc5df/ruff-0.11.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f2959049faeb5ba5e3b378709e9d1bf0cab06528b306b9dd6ebd2a312127964a", size = 10352151 }, + { url = "https://files.pythonhosted.org/packages/ff/91/f02839fb3787c678e112c8865f2c3e87cfe1744dcc96ff9fc56cfb97dda2/ruff-0.11.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63c5d4e30d9d0de7fedbfb3e9e20d134b73a30c1e74b596f40f0629d5c28a193", size = 10541951 }, + { url = "https://files.pythonhosted.org/packages/9e/f3/c09933306096ff7a08abede3cc2534d6fcf5529ccd26504c16bf363989b5/ruff-0.11.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:26a4b9a4e1439f7d0a091c6763a100cef8fbdc10d68593df6f3cfa5abdd9246e", size = 10079195 }, + { url = "https://files.pythonhosted.org/packages/e0/0d/a87f8933fccbc0d8c653cfbf44bedda69c9582ba09210a309c066794e2ee/ruff-0.11.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b5edf270223dd622218256569636dc3e708c2cb989242262fe378609eccf1308", size = 11698918 }, + { url = "https://files.pythonhosted.org/packages/52/7d/8eac0bd083ea8a0b55b7e4628428203441ca68cd55e0b67c135a4bc6e309/ruff-0.11.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f55844e818206a9dd31ff27f91385afb538067e2dc0beb05f82c293ab84f7d55", size = 12319426 }, + { url = "https://files.pythonhosted.org/packages/c2/dc/d0c17d875662d0c86fadcf4ca014ab2001f867621b793d5d7eef01b9dcce/ruff-0.11.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d8f782286c5ff562e4e00344f954b9320026d8e3fae2ba9e6948443fafd9ffc", size = 11791012 }, + { url = "https://files.pythonhosted.org/packages/f9/f3/81a1aea17f1065449a72509fc7ccc3659cf93148b136ff2a8291c4bc3ef1/ruff-0.11.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:01c63ba219514271cee955cd0adc26a4083df1956d57847978383b0e50ffd7d2", size = 13949947 }, + { url = "https://files.pythonhosted.org/packages/61/9f/a3e34de425a668284e7024ee6fd41f452f6fa9d817f1f3495b46e5e3a407/ruff-0.11.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15adac20ef2ca296dd3d8e2bedc6202ea6de81c091a74661c3666e5c4c223ff6", size = 11471753 }, + { url = "https://files.pythonhosted.org/packages/df/c5/4a57a86d12542c0f6e2744f262257b2aa5a3783098ec14e40f3e4b3a354a/ruff-0.11.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4dd6b09e98144ad7aec026f5588e493c65057d1b387dd937d7787baa531d9bc2", size = 10417121 }, + { url = "https://files.pythonhosted.org/packages/58/3f/a3b4346dff07ef5b862e2ba06d98fcbf71f66f04cf01d375e871382b5e4b/ruff-0.11.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:45b2e1d6c0eed89c248d024ea95074d0e09988d8e7b1dad8d3ab9a67017a5b03", size = 10073829 }, + { url = "https://files.pythonhosted.org/packages/93/cc/7ed02e0b86a649216b845b3ac66ed55d8aa86f5898c5f1691797f408fcb9/ruff-0.11.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:bd40de4115b2ec4850302f1a1d8067f42e70b4990b68838ccb9ccd9f110c5e8b", size = 11076108 }, + { url = "https://files.pythonhosted.org/packages/39/5e/5b09840fef0eff1a6fa1dea6296c07d09c17cb6fb94ed5593aa591b50460/ruff-0.11.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:77cda2dfbac1ab73aef5e514c4cbfc4ec1fbef4b84a44c736cc26f61b3814cd9", size = 11512366 }, + { url = "https://files.pythonhosted.org/packages/6f/4c/1cd5a84a412d3626335ae69f5f9de2bb554eea0faf46deb1f0cb48534042/ruff-0.11.6-py3-none-win32.whl", hash = "sha256:5151a871554be3036cd6e51d0ec6eef56334d74dfe1702de717a995ee3d5b287", size = 10485900 }, + { url = "https://files.pythonhosted.org/packages/42/46/8997872bc44d43df986491c18d4418f1caff03bc47b7f381261d62c23442/ruff-0.11.6-py3-none-win_amd64.whl", hash = "sha256:cce85721d09c51f3b782c331b0abd07e9d7d5f775840379c640606d3159cae0e", size = 11558592 }, + { url = "https://files.pythonhosted.org/packages/d7/6a/65fecd51a9ca19e1477c3879a7fda24f8904174d1275b419422ac00f6eee/ruff-0.11.6-py3-none-win_arm64.whl", hash = "sha256:3567ba0d07fb170b1b48d944715e3294b77f5b7679e8ba258199a250383ccb79", size = 10682766 }, +] + +[[package]] +name = "typing-extensions" +version = "4.13.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806 }, +] + +[[package]] +name = "yarl" +version = "1.20.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "multidict" }, + { name = "propcache" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/62/51/c0edba5219027f6eab262e139f73e2417b0f4efffa23bf562f6e18f76ca5/yarl-1.20.0.tar.gz", hash = "sha256:686d51e51ee5dfe62dec86e4866ee0e9ed66df700d55c828a615640adc885307", size = 185258 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/e8/3efdcb83073df978bb5b1a9cc0360ce596680e6c3fac01f2a994ccbb8939/yarl-1.20.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e06b9f6cdd772f9b665e5ba8161968e11e403774114420737f7884b5bd7bdf6f", size = 147089 }, + { url = "https://files.pythonhosted.org/packages/60/c3/9e776e98ea350f76f94dd80b408eaa54e5092643dbf65fd9babcffb60509/yarl-1.20.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b9ae2fbe54d859b3ade40290f60fe40e7f969d83d482e84d2c31b9bff03e359e", size = 97706 }, + { url = "https://files.pythonhosted.org/packages/0c/5b/45cdfb64a3b855ce074ae607b9fc40bc82e7613b94e7612b030255c93a09/yarl-1.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6d12b8945250d80c67688602c891237994d203d42427cb14e36d1a732eda480e", size = 95719 }, + { url = "https://files.pythonhosted.org/packages/2d/4e/929633b249611eeed04e2f861a14ed001acca3ef9ec2a984a757b1515889/yarl-1.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:087e9731884621b162a3e06dc0d2d626e1542a617f65ba7cc7aeab279d55ad33", size = 343972 }, + { url = "https://files.pythonhosted.org/packages/49/fd/047535d326c913f1a90407a3baf7ff535b10098611eaef2c527e32e81ca1/yarl-1.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:69df35468b66c1a6e6556248e6443ef0ec5f11a7a4428cf1f6281f1879220f58", size = 339639 }, + { url = "https://files.pythonhosted.org/packages/48/2f/11566f1176a78f4bafb0937c0072410b1b0d3640b297944a6a7a556e1d0b/yarl-1.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b2992fe29002fd0d4cbaea9428b09af9b8686a9024c840b8a2b8f4ea4abc16f", size = 353745 }, + { url = "https://files.pythonhosted.org/packages/26/17/07dfcf034d6ae8837b33988be66045dd52f878dfb1c4e8f80a7343f677be/yarl-1.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4c903e0b42aab48abfbac668b5a9d7b6938e721a6341751331bcd7553de2dcae", size = 354178 }, + { url = "https://files.pythonhosted.org/packages/15/45/212604d3142d84b4065d5f8cab6582ed3d78e4cc250568ef2a36fe1cf0a5/yarl-1.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf099e2432131093cc611623e0b0bcc399b8cddd9a91eded8bfb50402ec35018", size = 349219 }, + { url = "https://files.pythonhosted.org/packages/e6/e0/a10b30f294111c5f1c682461e9459935c17d467a760c21e1f7db400ff499/yarl-1.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a7f62f5dc70a6c763bec9ebf922be52aa22863d9496a9a30124d65b489ea672", size = 337266 }, + { url = "https://files.pythonhosted.org/packages/33/a6/6efa1d85a675d25a46a167f9f3e80104cde317dfdf7f53f112ae6b16a60a/yarl-1.20.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:54ac15a8b60382b2bcefd9a289ee26dc0920cf59b05368c9b2b72450751c6eb8", size = 360873 }, + { url = "https://files.pythonhosted.org/packages/77/67/c8ab718cb98dfa2ae9ba0f97bf3cbb7d45d37f13fe1fbad25ac92940954e/yarl-1.20.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:25b3bc0763a7aca16a0f1b5e8ef0f23829df11fb539a1b70476dcab28bd83da7", size = 360524 }, + { url = "https://files.pythonhosted.org/packages/bd/e8/c3f18660cea1bc73d9f8a2b3ef423def8dadbbae6c4afabdb920b73e0ead/yarl-1.20.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b2586e36dc070fc8fad6270f93242124df68b379c3a251af534030a4a33ef594", size = 365370 }, + { url = "https://files.pythonhosted.org/packages/c9/99/33f3b97b065e62ff2d52817155a89cfa030a1a9b43fee7843ef560ad9603/yarl-1.20.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:866349da9d8c5290cfefb7fcc47721e94de3f315433613e01b435473be63daa6", size = 373297 }, + { url = "https://files.pythonhosted.org/packages/3d/89/7519e79e264a5f08653d2446b26d4724b01198a93a74d2e259291d538ab1/yarl-1.20.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:33bb660b390a0554d41f8ebec5cd4475502d84104b27e9b42f5321c5192bfcd1", size = 378771 }, + { url = "https://files.pythonhosted.org/packages/3a/58/6c460bbb884abd2917c3eef6f663a4a873f8dc6f498561fc0ad92231c113/yarl-1.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:737e9f171e5a07031cbee5e9180f6ce21a6c599b9d4b2c24d35df20a52fabf4b", size = 375000 }, + { url = "https://files.pythonhosted.org/packages/3b/2a/dd7ed1aa23fea996834278d7ff178f215b24324ee527df53d45e34d21d28/yarl-1.20.0-cp312-cp312-win32.whl", hash = "sha256:839de4c574169b6598d47ad61534e6981979ca2c820ccb77bf70f4311dd2cc64", size = 86355 }, + { url = "https://files.pythonhosted.org/packages/ca/c6/333fe0338305c0ac1c16d5aa7cc4841208d3252bbe62172e0051006b5445/yarl-1.20.0-cp312-cp312-win_amd64.whl", hash = "sha256:3d7dbbe44b443b0c4aa0971cb07dcb2c2060e4a9bf8d1301140a33a93c98e18c", size = 92904 }, + { url = "https://files.pythonhosted.org/packages/ea/1f/70c57b3d7278e94ed22d85e09685d3f0a38ebdd8c5c73b65ba4c0d0fe002/yarl-1.20.0-py3-none-any.whl", hash = "sha256:5d0fe6af927a47a230f31e6004621fd0959eaa915fc62acfafa67ff7229a3124", size = 46124 }, +]