├── src ├── impl │ ├── bot │ │ ├── __init__.py │ │ ├── status.py │ │ └── bot.py │ └── __init__.py ├── __init__.py ├── lua │ ├── roulette.lua │ ├── use_chopsticks.lua │ ├── timely_reward.lua │ ├── see_inventory.lua │ ├── roulette_shot.lua │ ├── use_radar.lua │ ├── use_eggplant.lua │ ├── curse.lua │ ├── use_shoe.lua │ ├── pay.lua │ ├── gamble.lua │ ├── use_cross.lua │ ├── set_bomb.lua │ ├── farm_inventory.lua │ ├── ajo.lua │ ├── trade.lua │ ├── discombobulate.lua │ ├── craft.lua │ └── vampire.lua ├── __main__.py └── exts │ ├── vampires.py │ ├── general.py │ └── ajo.py ├── .env.example ├── .dockerignore ├── Dockerfile ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── feature-request.yml │ └── bug-report.yml └── workflows │ ├── del-dev.yml │ ├── docker.yml │ └── dev.yml ├── .pre-commit-config.yaml ├── .vscode └── launch.json ├── README.md ├── docker-compose.yml ├── util ├── test.py └── redis.conf ├── entrypoint.sh ├── pyproject.toml ├── LICENSE ├── .gitignore ├── CONTRIBUTING.md ├── bootstrap_items.py ├── ARCHITECTURE.md └── poetry.lock /src/impl/bot/__init__.py: -------------------------------------------------------------------------------- 1 | from .bot import Bot 2 | 3 | __all__ = ("Bot",) 4 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | TOKEN= 2 | REDIS_HOST=127.0.0.1 3 | TEST_GUILD= 4 | DEBUG= 5 | SENTRY_DSN= -------------------------------------------------------------------------------- /src/impl/__init__.py: -------------------------------------------------------------------------------- 1 | from .bot import Bot 2 | 3 | __all__ = ( 4 | "Bot" 5 | ) 6 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # Ignore all 2 | * 3 | 4 | # Include bootstrap 5 | !bootstrap_items.py 6 | 7 | # Include poetry 8 | !poetry.lock 9 | !pyproject.toml 10 | 11 | # Include source 12 | !src/* 13 | !entrypoint.sh 14 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM --platform=amd64 python:3.13-slim-bullseye 2 | 3 | WORKDIR /bot 4 | 5 | RUN apt update && apt install redis-tools -y 6 | RUN pip install poetry 7 | 8 | COPY pyproject.toml /bot 9 | COPY poetry.lock /bot 10 | 11 | RUN poetry install 12 | 13 | COPY . /bot 14 | 15 | CMD ["sh", "entrypoint.sh"] 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Installation Help 4 | url: https://discord.gg/DXEVMwdTVV 5 | about: Please visit our Discord for help with your installation. 6 | - name: General Question 7 | url: https://discord.gg/DXEVMwdTVV 8 | about: Please visit our Discord for general questions about Ajobot. -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pycqa/isort 3 | rev: "5.10.1" 4 | hooks: 5 | - id: isort 6 | args: ["--profile", "black", "."] 7 | name: Running isort in all files. 8 | 9 | - repo: https://github.com/psf/black 10 | rev: "21.12b0" 11 | hooks: 12 | - id: black 13 | args: ["--line-length=120"] 14 | name: Running black in all files. 15 | -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- 1 | from os import environ 2 | from dotenv import load_dotenv 3 | import sentry_sdk 4 | 5 | load_dotenv() 6 | 7 | from .impl import Bot 8 | 9 | __all__ = ("Bot",) 10 | 11 | sentry_sdk.init( 12 | dsn=environ.get('SENTRY_DSN'), 13 | 14 | # Set traces_sample_rate to 1.0 to capture 100% 15 | # of transactions for performance monitoring. 16 | # We recommend adjusting this value in production. 17 | traces_sample_rate=1.0 18 | ) -------------------------------------------------------------------------------- /src/lua/roulette.lua: -------------------------------------------------------------------------------- 1 | --! file: roulette.lua 2 | local group_key = KEYS[1] 3 | 4 | local expire = tonumber(ARGV[1]) 5 | 6 | redis.replicate_commands() 7 | 8 | -- sanity, does the roulette already exist? 9 | local shot = redis.call("exists", group_key) 10 | if tonumber(shot) ~= 0 then 11 | return {"err", false} 12 | end 13 | 14 | -- group expires in 10min, assign the shot number to that group 15 | redis.call("set", group_key, math.random(1, 6), "ex", expire) 16 | return {"OK", true} 17 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "src", 9 | "type": "debugpy", 10 | "request": "launch", 11 | "module": "src", 12 | "justMyCode": false 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ajo-bot 2 | 3 | ![https://www.ajobot.com/](https://www.ajobot.com/assets/images/ajologo.png) 4 | 5 | Farm ajos by spamming :garlic: in your discord guild. **Beware of the Vampire**. 6 | 7 | ## Invite the bot 8 | [Play for free](https://discord.com/api/oauth2/authorize?client_id=967138080375046214&permissions=265280&scope=bot%20applications.commands). 9 | 10 | ## Running the bot 11 | To run the bot you will need Poetry and Redis. 12 | 13 | ```sh 14 | poetry install 15 | poetry run task start 16 | ``` 17 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | bot: 5 | build: . 6 | restart: always 7 | env_file: .env 8 | depends_on: 9 | - redis 10 | redis: 11 | image: redis:7.2 12 | restart: unless-stopped 13 | command: ["redis-server", "/usr/local/etc/redis/redis.conf"] 14 | ports: 15 | - '6379:6379' 16 | volumes: 17 | - ./redis_data:/data 18 | - ./utils/redis.conf:/usr/local/etc/redis/redis.conf:ro 19 | volumes: 20 | redis_data: 21 | driver: local 22 | -------------------------------------------------------------------------------- /util/test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import time, os, math 4 | import redis 5 | 6 | r = redis.Redis(host='127.0.0.1') 7 | 8 | #r.flushdb(asynchronous=False) 9 | 10 | i=0 11 | while True: 12 | seed_1 = time.time_ns()-(int(time.time())*1000000000) 13 | #seed_2 = str(int.from_bytes(os.urandom(16), 'big')) 14 | #seed_3 = seed_1[::-1] 15 | result = r.evalsha('14ad935184344bc46e4a73f6c51e93d27b26b6ee', '3', "ajobus-inventory", "lb", "468516898309537792:inventory", "468516898309537792", seed_1) 16 | print(f"{i}: {result}") 17 | i = i+1 -------------------------------------------------------------------------------- /entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -euf 3 | 4 | REDIS_HOST="${REDIS_HOST:-127.0.0.1}" 5 | 6 | # load scripts into memory 7 | PATHS="discombobulate gamble pay timely_reward roulette roulette_shot" 8 | PATHS="${PATHS} farm_inventory use_chopsticks use_cross craft" 9 | PATHS="${PATHS} trade see_inventory vampire ajo use_shoe use_eggplant" 10 | PATHS="${PATHS} set_bomb curse use_radar" 11 | for path in ${PATHS}; do 12 | sha=$(redis-cli -h ${REDIS_HOST} -x script load < src/lua/$path.lua) 13 | grep "$path=" .env > /dev/null 2>&1 || echo "$path=" >> .env 14 | sed -i "s/\($path\)=\(.\+\)\?/\1=$sha/" .env 15 | done 16 | 17 | # bootstrap item data 18 | python bootstrap_items.py | redis-cli -h ${REDIS_HOST} --pipe >/dev/null 19 | 20 | poetry run task start 21 | -------------------------------------------------------------------------------- /src/lua/use_chopsticks.lua: -------------------------------------------------------------------------------- 1 | --! file: use_chopsticks.lua 2 | local strm_key = KEYS[1] 3 | local inventory_key = KEYS[2] 4 | local vampire_key = KEYS[3] 5 | 6 | local id = ARGV[1] 7 | local item = ARGV[2] 8 | local event_version = ARGV[3] 9 | local guild_id = ARGV[4] 10 | 11 | -- ensure we actually own the item 12 | local stack = tonumber(redis.call("hget", inventory_key, item)) 13 | if not stack or stack < 1 then 14 | return {"err", false} 15 | end 16 | 17 | -- decrease stack and vampire level 18 | redis.call("hincrby", inventory_key, item, -1) 19 | redis.call( 20 | "xadd", strm_key, "*", 21 | "version", event_version, 22 | "type", "item_used", 23 | "user_id", id, 24 | "guild_id", guild_id, 25 | "item", item, 26 | "quantity", -1 27 | ) 28 | 29 | redis.call("del", vampire_key) 30 | return {"OK", 1} 31 | -------------------------------------------------------------------------------- /src/lua/timely_reward.lua: -------------------------------------------------------------------------------- 1 | --! file: timely_reward.lua 2 | local strm_key = KEYS[1] 3 | local lb_key = KEYS[2] 4 | local timely_key = KEYS[3] 5 | 6 | local id = ARGV[1] 7 | local reward = math.ceil(tonumber(ARGV[2])) 8 | local expire = tonumber(ARGV[3]) 9 | local event_version = ARGV[4] 10 | local guild_id = ARGV[5] 11 | 12 | -- is this reward available? 13 | local ttl = tonumber(redis.call("ttl", timely_key)) 14 | if ttl > 0 then 15 | return {"ttl", ttl} 16 | end 17 | 18 | redis.call("zincrby", lb_key, reward, id) 19 | redis.call("set", timely_key, 1, "ex", expire) 20 | 21 | -- append data to stream 22 | redis.call( 23 | "xadd", strm_key, "*", 24 | "version", event_version, 25 | "type", "timely_reward", 26 | "user_id", id, 27 | "guild_id", guild_id, 28 | "amount", reward 29 | ) 30 | return {"OK", reward} 31 | -------------------------------------------------------------------------------- /src/lua/see_inventory.lua: -------------------------------------------------------------------------------- 1 | --! file: see_inventory.lua 2 | local strm_key = KEYS[1] 3 | local lb_key = KEYS[2] 4 | local inventory_key = KEYS[3] 5 | 6 | local id = ARGV[1] 7 | local event_version = ARGV[2] 8 | local guild_id = ARGV[3] 9 | 10 | -- can we pay to see the inventory? 11 | local fee = 10 12 | local current = tonumber(redis.call("zscore", lb_key, id)) 13 | if not current or current < fee then 14 | return {"funds", fee} 15 | end 16 | 17 | -- retrieve the inentory values 18 | local items = redis.call("hgetall", inventory_key) 19 | 20 | -- pay the fee and append to stream 21 | redis.call("zincrby", lb_key, -fee, id) 22 | redis.call( 23 | "xadd", strm_key, "*", 24 | "version", event_version, 25 | "type", "inventory_fee", 26 | "user_id", id, 27 | "guild_id", guild_id, 28 | "amount", -fee 29 | ) 30 | return {"OK", items} 31 | -------------------------------------------------------------------------------- /src/__main__.py: -------------------------------------------------------------------------------- 1 | from os import environ 2 | 3 | from disnake import AllowedMentions, Intents 4 | 5 | from . import Bot 6 | 7 | 8 | def main() -> None: 9 | test_guilds: list[int] | None = None 10 | 11 | if guild := environ.get("TEST_GUILD"): 12 | test_guilds = [int(guild)] 13 | 14 | intents = Intents(message_content=True, guilds=True) 15 | intents.messages = True 16 | intents.members = True 17 | 18 | bot = Bot( 19 | test_guilds=test_guilds, 20 | intents=intents, 21 | command_prefix=("a/", "ajo ", "ajo/"), 22 | help_command=None, 23 | case_insensitive=True, 24 | allowed_mentions=AllowedMentions.none(), 25 | ) 26 | 27 | for ext in [ 28 | "src.exts.ajo", 29 | "src.exts.general", 30 | "src.exts.vampires", 31 | ]: 32 | bot.load_extension(ext) 33 | 34 | bot.run(environ["TOKEN"]) 35 | 36 | 37 | main() 38 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "ajo-bot" 3 | version = "1.0.0" 4 | description = "Count your ajo stats!" 5 | authors = ["axel "] 6 | license = "MIT" 7 | repository = "https://github.com/lastlife/ajobot" 8 | package-mode = false 9 | 10 | [tool.poetry.dependencies] 11 | python = "^3.10" 12 | taskipy = "^1.9.0" 13 | disnake = "^2.4.0" 14 | loguru = "^0.6.0" 15 | python-dotenv = "^0.21.0" 16 | ajobot-manager-v5 = "^1.3.1" 17 | sentry-sdk = "^1.9.8" 18 | setuptools = "^80.1.0" 19 | redis = "^7.1.0" 20 | 21 | [tool.poetry.dev-dependencies] 22 | black = "^21.12b0" 23 | isort = "^5.10.1" 24 | pre-commit = "^2.16.0" 25 | 26 | [tool.taskipy.tasks] 27 | start = "python -m src" 28 | lint = "black . && isort --profile black ." 29 | 30 | [tool.black] 31 | line-length = 120 32 | 33 | [tool.pyright] 34 | 35 | [build-system] 36 | requires = ["poetry-core>=1.0.0"] 37 | build-backend = "poetry.core.masonry.api" 38 | -------------------------------------------------------------------------------- /src/lua/roulette_shot.lua: -------------------------------------------------------------------------------- 1 | --! file: roulette_shot.lua 2 | local strm_key = KEYS[1] 3 | local lb_key = KEYS[2] 4 | local group_key = KEYS[3] 5 | 6 | local id = ARGV[1] 7 | local event_version = ARGV[2] 8 | local guild_id = ARGV[3] 9 | 10 | -- sanity, does the roulette already exist? 11 | local shot = redis.call("exists", group_key) 12 | if tonumber(shot) == 0 then 13 | return {"err", false} 14 | end 15 | 16 | -- decrement the group key 17 | local rem = redis.call("decrby", group_key, 1) 18 | if rem > 0 then 19 | return {"OK", true} 20 | end 21 | 22 | -- kill the person, remove the roulette 23 | local dmg = redis.call("zscore", lb_key, id) 24 | redis.call("zrem", lb_key, id) 25 | redis.call("del", group_key) 26 | 27 | -- append data to stream 28 | redis.call( 29 | "xadd", strm_key, "*", 30 | "version", event_version, 31 | "type", "ded", 32 | "user_id", id, 33 | "guild_id", guild_id, 34 | "amount", -dmg 35 | ) 36 | return {"shot", true} 37 | -------------------------------------------------------------------------------- /.github/workflows/del-dev.yml: -------------------------------------------------------------------------------- 1 | name: del-dev-env 2 | on: 3 | workflow_dispatch: 4 | inputs: 5 | name: 6 | description: 'Deployment name, it needs to exist to success deletion' 7 | required: true 8 | 9 | jobs: 10 | delete: 11 | name: delete dev deployment 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: delete 15 | uses: WyriHaximus/github-action-helm3@v2 16 | with: 17 | exec: | 18 | helm repo add ajobot https://charts.ajobot.com 19 | helm repo update 20 | helm uninstall dev-${{ github.event.inputs.name }} -n ajotest 21 | kubeconfig: '${{ secrets.KUBECONFIG }}' 22 | - uses: castlabs/get-package-version-id-action@v2.0 23 | id: versions 24 | with: 25 | version: "dev-${{ github.event.inputs.name }}" 26 | - uses: actions/delete-package-versions@v2 27 | if: ${{ steps.versions.outputs.ids != '' }} 28 | with: 29 | package-version-ids: "${{ steps.versions.outputs.ids }}" 30 | -------------------------------------------------------------------------------- /src/lua/use_radar.lua: -------------------------------------------------------------------------------- 1 | --! file: use_radar.lua 2 | local strm_key = KEYS[1] 3 | local inventory_key = KEYS[2] 4 | local cron_key = KEYS[3] 5 | 6 | local id = ARGV[1] 7 | local item = ARGV[2] 8 | local event_version = ARGV[3] 9 | local guild_id = ARGV[4] 10 | 11 | -- ensure we actually own the item 12 | local stack = tonumber(redis.call("hget", inventory_key, item)) 13 | if not stack or stack < 1 then 14 | return {"err", false} 15 | end 16 | 17 | -- decrease stack 18 | redis.call("hincrby", inventory_key, item, -1) 19 | redis.call( 20 | "xadd", strm_key, "*", 21 | "version", event_version, 22 | "type", "item_used", 23 | "user_id", id, 24 | "guild_id", guild_id, 25 | "item", item, 26 | "quantity", -1 27 | ) 28 | 29 | -- the radar shows the next five bomb to detonate, if any 30 | local bombs = redis.call( 31 | "zrange", 32 | cron_key, 33 | "0", 34 | "+inf", 35 | "byscore", 36 | "limit", 37 | 0, 38 | 5, 39 | "withscores" 40 | ) 41 | return {"OK", bombs} 42 | -------------------------------------------------------------------------------- /src/lua/use_eggplant.lua: -------------------------------------------------------------------------------- 1 | --! file: use_eggplant.lua 2 | local strm_key = KEYS[1] 3 | local inventory_key = KEYS[2] 4 | local item_key = KEYS[3] 5 | local buff_key = KEYS[4] 6 | 7 | local id = ARGV[1] 8 | local item = ARGV[2] 9 | local event_version = ARGV[3] 10 | local guild_id = ARGV[4] 11 | 12 | -- ensure we actually own the item 13 | local stack = tonumber(redis.call("hget", inventory_key, item)) 14 | if not stack or stack < 1 then 15 | return "err" 16 | end 17 | 18 | -- the eggplant sets up a buff, find its value and ttl 19 | local item_data = redis.call("hmget", item_key, "buff", "ttl") 20 | if not item_data then 21 | return "err" 22 | end 23 | 24 | -- decrease stack 25 | redis.call("hincrby", inventory_key, item, -1) 26 | redis.call( 27 | "xadd", strm_key, "*", 28 | "version", event_version, 29 | "type", "item_used", 30 | "user_id", id, 31 | "guild_id", guild_id, 32 | "item", item, 33 | "quantity", -1 34 | ) 35 | 36 | local buff = tonumber(item_data[1]) 37 | local ttl = tonumber(item_data[2]) 38 | redis.call("set", buff_key, buff, "EX", ttl) 39 | return "OK" 40 | -------------------------------------------------------------------------------- /src/lua/curse.lua: -------------------------------------------------------------------------------- 1 | --! file: curse.lua 2 | local strm_key = KEYS[1] 3 | local inventory_key = KEYS[2] 4 | local item_key = KEYS[3] 5 | local curse_key = KEYS[4] 6 | 7 | local id = ARGV[1] 8 | local item = ARGV[2] 9 | local event_version = ARGV[3] 10 | local guild_id = ARGV[4] 11 | 12 | -- ensure we actually own the item 13 | local stack = tonumber(redis.call("hget", inventory_key, item)) 14 | if not stack or stack < 1 then 15 | return "err" 16 | end 17 | 18 | -- the magic wand sets up a curse, find its value and ttl 19 | local item_data = redis.call("hmget", item_key, "curse", "ttl") 20 | if not item_data then 21 | return "err" 22 | end 23 | 24 | -- use the item, decrease stack 25 | redis.call("hincrby", inventory_key, item, -1) 26 | redis.call( 27 | "xadd", strm_key, "*", 28 | "version", event_version, 29 | "type", "item_used", 30 | "user_id", id, 31 | "guild_id", guild_id, 32 | "item", item, 33 | "quantity", -1 34 | ) 35 | 36 | local curse = tonumber(item_data[1]) 37 | local ttl = tonumber(item_data[2]) 38 | redis.call("set", curse_key, curse, "EX", ttl) 39 | return "OK" 40 | -------------------------------------------------------------------------------- /src/lua/use_shoe.lua: -------------------------------------------------------------------------------- 1 | --! file: use_shoe.lua 2 | local strm_key = KEYS[1] 3 | local inventory_key = KEYS[2] 4 | local item_key = KEYS[3] 5 | local ajo_gain_key = KEYS[4] 6 | 7 | local id = ARGV[1] 8 | local item = ARGV[2] 9 | local event_version = ARGV[3] 10 | local guild_id = ARGV[4] 11 | 12 | -- ensure we actually own the item 13 | local stack = tonumber(redis.call("hget", inventory_key, item)) 14 | if not stack or stack < 1 then 15 | return "err" 16 | end 17 | 18 | -- decrease stack 19 | redis.call("hincrby", inventory_key, item, -1) 20 | redis.call( 21 | "xadd", strm_key, "*", 22 | "version", event_version, 23 | "type", "item_used", 24 | "user_id", id, 25 | "guild_id", guild_id, 26 | "item", item, 27 | "quantity", -1 28 | ) 29 | 30 | -- the shoe increases our ajo gain by 1 (100%) 31 | local ajo_gain = tonumber(redis.call("get", ajo_gain_key)) 32 | if not ajo_gain then 33 | ajo_gain = 2 34 | else 35 | ajo_gain = ajo_gain + 1 36 | end 37 | 38 | -- does this item expire? 39 | local ttl = tonumber(redis.call("hget", item_key, "ttl")) 40 | if not ttl then 41 | ttl = 600 42 | end 43 | 44 | redis.call("set", ajo_gain_key, ajo_gain, "EX", ttl) 45 | return "OK" 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 vcokltfre 4 | Copyright (c) 2022 axl89, fabri2000779, lastlife, Zymna 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /src/impl/bot/status.py: -------------------------------------------------------------------------------- 1 | from asyncio import Task, create_task, sleep 2 | from os import getenv 3 | 4 | from aiohttp import ClientSession 5 | from loguru import logger 6 | 7 | 8 | class StatusHeartbeater: 9 | def __init__(self) -> None: 10 | self._call_uri: str | None = getenv("STATUS_CALL_URI") 11 | self._task: Task[None] | None = None 12 | self._session: ClientSession | None = None 13 | 14 | async def _heartbeat(self) -> None: 15 | while True: 16 | try: 17 | if self._call_uri: 18 | if not self._session: 19 | self._session = ClientSession() 20 | 21 | await self._session.get(self._call_uri) 22 | logger.info(f"Sent status heartbeat.") 23 | 24 | await sleep(45) 25 | except Exception as e: 26 | logger.error(f"Failed to send status heartbeat: {e}") 27 | 28 | await sleep(10) 29 | 30 | def run(self) -> None: 31 | if self._call_uri: 32 | self._task = create_task(self._heartbeat()) 33 | 34 | def stop(self) -> None: 35 | if self._task: 36 | self._task.cancel() 37 | -------------------------------------------------------------------------------- /src/lua/pay.lua: -------------------------------------------------------------------------------- 1 | --! file: pay.lua 2 | local strm_key = KEYS[1] 3 | local lb_key = KEYS[2] 4 | 5 | local source_id = ARGV[1] 6 | local target_id = ARGV[2] 7 | local amount = math.ceil(tonumber(ARGV[3])) 8 | local event_version = ARGV[4] 9 | local guild_id = ARGV[5] 10 | 11 | -- sanity checks 12 | if amount < 1 then 13 | return {"err", false} 14 | end 15 | 16 | if source_id == target_id then 17 | return {"futile", false} 18 | end 19 | 20 | -- can we pay that much? 21 | local source_amount = tonumber(redis.call("zscore", lb_key, source_id)) 22 | if not source_amount or source_amount < amount then 23 | return {"funds", false} 24 | end 25 | 26 | redis.call("zincrby", lb_key, -amount, source_id) 27 | redis.call("zincrby", lb_key, amount, target_id) 28 | -- 29 | -- append data to stream 30 | redis.call( 31 | "xadd", strm_key, "*", 32 | "version", event_version, 33 | "type", "payer", 34 | "user_id", source_id, 35 | "guild_id", guild_id, 36 | "amount", -amount 37 | ) 38 | redis.call( 39 | "xadd", strm_key, "*", 40 | "version", event_version, 41 | "type", "payee", 42 | "user_id", target_id, 43 | "guild_id", guild_id, 44 | "amount", amount 45 | ) 46 | return {"OK", amount} 47 | -------------------------------------------------------------------------------- /.github/workflows/docker.yml: -------------------------------------------------------------------------------- 1 | name: Build and Deploy 2 | on: 3 | push: 4 | tags: 5 | - v* 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v3 11 | - uses: docker/login-action@v2 12 | with: 13 | registry: ghcr.io 14 | username: ${{ github.actor }} 15 | password: ${{ secrets.GITHUB_TOKEN }} 16 | - name: Fetch metadata 17 | id: metadata 18 | uses: docker/metadata-action@v4 19 | with: 20 | images: ghcr.io/ass-a-service/ajobot 21 | - name: build and push 22 | uses: docker/build-push-action@v4 23 | with: 24 | push: true 25 | tags: ${{ steps.metadata.outputs.tags }} 26 | labels: ${{ steps.metadata.outputs.labels }} 27 | deploy: 28 | name: deploy to cluster 29 | needs: build 30 | runs-on: ubuntu-latest 31 | steps: 32 | - name: Deploy 33 | uses: WyriHaximus/github-action-helm3@v3 34 | with: 35 | exec: | 36 | helm repo add ajobot https://charts.ajobot.com 37 | helm repo update 38 | helm upgrade ajobot ajobot/ajobot -n ajobot --reuse-values --set image.tag=${{github.ref_name}} 39 | kubeconfig: '${{ secrets.KUBECONFIG }}' 40 | -------------------------------------------------------------------------------- /src/lua/gamble.lua: -------------------------------------------------------------------------------- 1 | --! file: gamble.lua 2 | local strm_key = KEYS[1] 3 | local lb_key = KEYS[2] 4 | 5 | local id = ARGV[1] 6 | local amount = ARGV[2] 7 | local event_version = ARGV[3] 8 | local guild_id = ARGV[4] 9 | 10 | redis.replicate_commands() 11 | 12 | -- check for gamble all 13 | local current = tonumber(redis.call("zscore", lb_key, id)) 14 | if amount == "all" then 15 | amount = current 16 | else 17 | -- sanity checks 18 | amount = tonumber(amount) 19 | if not amount or amount < 1 then 20 | return {"err", false} 21 | end 22 | 23 | amount = math.ceil(amount) 24 | end 25 | 26 | -- can we gamble that much? 27 | if not current or current < amount then 28 | return {"funds", false} 29 | end 30 | 31 | -- 25% chance to win up from 1% to 250% 32 | local change 33 | if math.random(0, 3) == 1 then 34 | change = math.ceil(math.random(1, 100) / 40 * amount) 35 | else 36 | change = -amount 37 | end 38 | 39 | redis.call("zincrby", lb_key, change, id) 40 | 41 | -- append data to stream 42 | redis.call( 43 | "xadd", strm_key, "*", 44 | "version", event_version, 45 | "type", "gamble", 46 | "user_id", id, 47 | "guild_id", guild_id, 48 | "amount", change 49 | ) 50 | return {"OK", change} 51 | -------------------------------------------------------------------------------- /src/lua/use_cross.lua: -------------------------------------------------------------------------------- 1 | --! file: use_cross.lua 2 | local strm_key = KEYS[1] 3 | local inventory_key = KEYS[2] 4 | local vampire_key = KEYS[3] 5 | 6 | local id = ARGV[1] 7 | local item = ARGV[2] 8 | local event_version = ARGV[3] 9 | local guild_id = ARGV[4] 10 | 11 | local ttl_per_level = 600 12 | 13 | -- ensure we actually own the item 14 | local stack = tonumber(redis.call("hget", inventory_key, item)) 15 | if not stack or stack < 1 then 16 | return {"err", false} 17 | end 18 | 19 | -- use the item, decrease stack 20 | redis.call("hincrby", inventory_key, item, -1) 21 | redis.call( 22 | "xadd", strm_key, "*", 23 | "version", event_version, 24 | "type", "item_used", 25 | "user_id", id, 26 | "guild_id", guild_id, 27 | "item", item, 28 | "quantity", -1 29 | ) 30 | 31 | -- the vampire level in redis is the level which will appear next, not current 32 | local vampire_level = tonumber(redis.call("get", vampire_key)) 33 | if not vampire_level or vampire_level < 2 then 34 | -- if there's no vampire or the level is default, nothing to do 35 | return {"OK", 1} 36 | end 37 | 38 | -- reduce the vampire and refresh the ttl 39 | local new_level = vampire_level - 1 40 | local ttl = math.min(ttl_per_level * new_level, 7200) 41 | redis.call("set", vampire_key, new_level, "EX", ttl) 42 | return {"OK", new_level} 43 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Suggest a new feature or improvement. 3 | labels: [feature request] 4 | body: 5 | - type: checkboxes 6 | attributes: 7 | label: Is there an existing feature request for this? 8 | description: Please [search here](https://github.com/ass-a-service/ajobot/issues?q=is%3Aissue) to see if someone else has already suggested this. 9 | options: 10 | - label: I have searched the existing issues before opening this feature request. 11 | required: true 12 | 13 | - type: textarea 14 | attributes: 15 | label: Describe the feature you would like to see. 16 | description: "A clear & concise description of the feature you'd like to have added, and what issues it would solve." 17 | validations: 18 | required: true 19 | 20 | - type: textarea 21 | attributes: 22 | label: Describe the solution you'd like. 23 | description: "You must explain how you'd like to see this feature implemented. Technical implementation details are not necessary, rather an idea of how you'd like to see this feature used." 24 | validations: 25 | required: true 26 | 27 | - type: textarea 28 | attributes: 29 | label: Additional context to this request. 30 | description: "Add any other context or screenshots about the feature request." 31 | validations: 32 | required: false -------------------------------------------------------------------------------- /util/redis.conf: -------------------------------------------------------------------------------- 1 | ######################################## 2 | # General 3 | ######################################## 4 | daemonize no 5 | protected-mode yes 6 | bind 0.0.0.0 7 | port 6379 8 | 9 | ######################################## 10 | # Memory & Performance 11 | ######################################## 12 | maxmemory 0 13 | maxmemory-policy noeviction 14 | io-threads 4 15 | io-threads-do-reads yes 16 | 17 | ######################################## 18 | # Persistence (AOF-focused) 19 | ######################################## 20 | # Enable Append-Only File 21 | appendonly yes 22 | 23 | # Most balanced fsync mode: async writes, fsync every second. 24 | # (If you want no data loss ever: set to "always") 25 | appendfsync everysec 26 | 27 | # Auto-rewrite when AOF grows too much 28 | auto-aof-rewrite-percentage 100 29 | auto-aof-rewrite-min-size 64mb 30 | 31 | # Use faster and safer RDB+AOF hybrid format 32 | aof-use-rdb-preamble yes 33 | 34 | ######################################## 35 | # Snapshotting (RDB fallback) 36 | ######################################## 37 | save 900 1 38 | save 300 10 39 | save 60 10000 40 | 41 | ######################################## 42 | # Networking & Safety 43 | ######################################## 44 | tcp-keepalive 300 45 | timeout 0 46 | 47 | ######################################## 48 | # Logging 49 | ######################################## 50 | loglevel notice 51 | -------------------------------------------------------------------------------- /src/impl/bot/bot.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Optional 2 | 3 | from disnake.ext.commands import Bot as _Bot 4 | from loguru import logger 5 | 6 | from ajobot_manager.manager import AjoManager 7 | 8 | from .status import StatusHeartbeater 9 | 10 | 11 | class Bot(_Bot): 12 | def __init__(self, *args: Any, **kwargs: Any) -> None: 13 | super().__init__(*args, **kwargs) # type: ignore 14 | 15 | self._status = StatusHeartbeater() 16 | self.manager = AjoManager() 17 | 18 | async def start(self, *args: Any, reconnect: bool = True, **kwargs: Any) -> None: 19 | 20 | self._status.run() 21 | 22 | await super().start(*args, reconnect=reconnect, **kwargs) 23 | 24 | async def on_connect(self) -> None: 25 | logger.info("Connected to the Discord Gateway.") 26 | 27 | async def on_error(self, event_method: str, *args: Any, **kwargs: Any) -> None: 28 | raise 29 | 30 | async def on_ready(self) -> None: 31 | logger.info(f"READY event received, connected as {self.user} with {len(self.guilds)} guilds.") 32 | 33 | async def on_guild_join(self, guild) -> None: 34 | logger.info(f"Guild {guild} added me. Realoading now.") 35 | self.reload = True 36 | 37 | def load_extension(self, name: str, *, package: Optional[str] = None) -> None: 38 | super().load_extension(name, package=package) 39 | 40 | logger.info(f"Loaded extension {name}.") 41 | -------------------------------------------------------------------------------- /src/lua/set_bomb.lua: -------------------------------------------------------------------------------- 1 | --! file: set_bomb.lua 2 | local strm_key = KEYS[1] 3 | local inventory_key = KEYS[2] 4 | local item_key = KEYS[3] 5 | local cron_key = KEYS[4] 6 | 7 | local ttl = tonumber(ARGV[1]) 8 | local id = ARGV[2] 9 | local item = ARGV[3] 10 | local event_version = ARGV[4] 11 | local guild_id = ARGV[5] 12 | 13 | redis.replicate_commands() 14 | 15 | -- sanity check, the bomb only allows timer for a maximum of 8h 16 | if ttl < 0 or ttl > 28800 then 17 | return {"time", false} 18 | end 19 | 20 | -- ensure we actually own the item 21 | local stack = tonumber(redis.call("hget", inventory_key, item)) 22 | if not stack or stack < 1 then 23 | return {"err", false} 24 | end 25 | 26 | -- decrease stack 27 | redis.call("hincrby", inventory_key, item, -1) 28 | redis.call( 29 | "xadd", strm_key, "*", 30 | "version", event_version, 31 | "type", "item_used", 32 | "user_id", id, 33 | "guild_id", guild_id, 34 | "item", item, 35 | "quantity", -1 36 | ) 37 | 38 | local time = redis.call("time") 39 | local expected = tonumber(time[1]) + ttl 40 | 41 | -- the bomb will set off within a gap of 7min of the set time 42 | local actual = math.random(expected - 210, expected + 210) 43 | 44 | -- add the bomb to the cron key, its identifier is the user's ID, meaning a user 45 | -- can only set one bomb at a time. An existing bomb will be overwritten 46 | redis.call("zadd", cron_key, actual, id) 47 | return {"OK", expected} 48 | -------------------------------------------------------------------------------- /.github/workflows/dev.yml: -------------------------------------------------------------------------------- 1 | name: dev-env 2 | on: 3 | workflow_dispatch: 4 | inputs: 5 | name: 6 | description: 'Deployment name' 7 | required: true 8 | botToken: 9 | description: 'Enter the bot discord token here. You can find it in discord developers.' 10 | required: true 11 | debug: 12 | type: choice 13 | description: if you set it in 1 you enable debug mode meaning you will be able to give you ajos, vampire level and more. 14 | options: 15 | - 0 16 | - 1 17 | 18 | jobs: 19 | build: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: actions/checkout@v3 23 | - uses: docker/login-action@v1 24 | with: 25 | registry: ghcr.io 26 | username: ${{ github.actor }} 27 | password: ${{ secrets.GITHUB_TOKEN }} 28 | - uses: crazy-max/ghaction-docker-meta@v1 29 | id: docker_meta 30 | with: 31 | images: ghcr.io/ass-a-service/ajobot 32 | - name: build and push 33 | uses: docker/build-push-action@v2 34 | with: 35 | push: true 36 | tags: ghcr.io/ass-a-service/ajobot:dev-${{ github.event.inputs.name }} 37 | labels: ${{ steps.docker_meta.outputs.labels }} 38 | deploy: 39 | name: deploy to cluster 40 | needs: build 41 | runs-on: ubuntu-latest 42 | steps: 43 | - name: Deploy 44 | uses: WyriHaximus/github-action-helm3@v2 45 | with: 46 | exec: | 47 | helm repo add ajobot https://charts.ajobot.com 48 | helm repo update 49 | helm upgrade --install dev-${{ github.event.inputs.name }} ajobot/ajobot -n ajotest --set image.tag=dev-${{ github.event.inputs.name }} --set botToken=${{ github.event.inputs.botToken }} --set debug=${{ github.event.inputs.debug }} 50 | kubeconfig: '${{ secrets.KUBECONFIG }}' 51 | -------------------------------------------------------------------------------- /src/exts/vampires.py: -------------------------------------------------------------------------------- 1 | from math import ceil, log 2 | from os import environ 3 | import time 4 | 5 | from disnake import Message, User 6 | from disnake.ext.commands import Cog 7 | 8 | from src.impl.bot import Bot 9 | 10 | LEADERBOARD = "lb" 11 | AJOBUS = "ajobus" 12 | EVENT_VERSION = 1 13 | 14 | class Vampires(Cog): 15 | def __init__(self, bot: Bot) -> None: 16 | self.bot = bot 17 | 18 | @Cog.listener() 19 | async def on_message(self, message: Message) -> None: 20 | if message.author.bot or message.guild is None: 21 | return 22 | 23 | contains_ajo = await self.bot.manager.contains_ajo(message.content) 24 | if not contains_ajo: 25 | return 26 | 27 | vampire_key = f"{message.author.id}:vampire" 28 | curse_key = f"{message.author.id}:wand-curse" 29 | inventory_key = f"{message.author.id}:inventory" 30 | debug_key = "ajodebug" 31 | 32 | err, res = await self.bot.manager.redis.evalsha( 33 | environ["vampire"], 34 | 6, 35 | AJOBUS, 36 | LEADERBOARD, 37 | vampire_key, 38 | curse_key, 39 | inventory_key, 40 | debug_key, 41 | message.author.id, 42 | EVENT_VERSION, 43 | 0 if message.guild is None else message.guild.id, 44 | time.time_ns()-(int(time.time())*1000000000) 45 | ) 46 | 47 | if not res: 48 | return 49 | 50 | vampire_level, used_ajos = res 51 | if err == b'NECKLACE': 52 | msg = f"A vampire level {vampire_level} has appeared! You use your ajo necklace to scare him away." 53 | else: 54 | msg = f"A vampire level {vampire_level} has appeared! You use {used_ajos} ajos to defeat him. You are safe... for now." 55 | 56 | await message.reply(msg) 57 | 58 | def setup(bot: Bot) -> None: 59 | bot.add_cog(Vampires(bot)) 60 | -------------------------------------------------------------------------------- /src/lua/farm_inventory.lua: -------------------------------------------------------------------------------- 1 | --! file: farm_inventory.lua 2 | local strm_key = KEYS[1] 3 | local items_key = KEYS[2] 4 | local lb_key = KEYS[3] 5 | local inventory_key = KEYS[4] 6 | 7 | local id = ARGV[1] 8 | local event_version = ARGV[2] 9 | local guild_id = ARGV[3] 10 | local event_id = ARGV[4] -- the redis event_id trigger 11 | 12 | redis.replicate_commands() 13 | 14 | -- you need at least some ajos to farm 15 | -- avoids the spam with 0 ajos 16 | local min_ajos = 50 17 | local current = tonumber(redis.call("zscore", lb_key, id)) 18 | if not current or current < min_ajos then 19 | return {"funds", current} 20 | end 21 | 22 | -- retrieve redis drop rate and maxstack data 23 | local rand = math.random(1, 100000) 24 | local acc = 0 25 | local item, drop_rate, max_stack 26 | 27 | -- retrieve our drop rate from redis 28 | local vals = redis.call("lrange", items_key, 0, -1) 29 | local size = #vals 30 | local index = 1 31 | while index < size do 32 | item = vals[index] 33 | drop_rate = tonumber(vals[index + 1]) 34 | max_stack = tonumber(vals[index + 2]) 35 | index = index + 3 36 | 37 | -- increase our chance of receiving something 38 | acc = acc + drop_rate 39 | 40 | -- if we're lower than the current chance, we got the item 41 | if rand <= acc then 42 | -- ensure we did not reach max stack 43 | local stack = tonumber(redis.call("hget", inventory_key, item)) 44 | if not stack or stack < max_stack then 45 | stack = redis.call("hincrby", inventory_key, item, 1) 46 | redis.call( 47 | "xadd", strm_key, "*", 48 | "version", event_version, 49 | "type", "item_earned", 50 | "user_id", id, 51 | "guild_id", guild_id, 52 | "event_id", event_id, 53 | "item", item, 54 | "quantity", 1 55 | ) 56 | return {"OK", {item, stack}} 57 | end 58 | 59 | return {"stack", false} 60 | end 61 | end 62 | 63 | return {"none", false} 64 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Something isn't working quite right. 3 | labels: [not confirmed] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Bug reports should only be used for reporting issues with how the software works. For assistance installing this software, please use our [Discord server](https://discord.gg/DXEVMwdTVV). 9 | 10 | - type: textarea 11 | attributes: 12 | label: Current Behavior 13 | description: Please provide a clear & concise description of the issue. 14 | validations: 15 | required: true 16 | 17 | - type: textarea 18 | attributes: 19 | label: Expected Behavior 20 | description: Please describe what you expected to happen. 21 | validations: 22 | required: true 23 | 24 | - type: textarea 25 | attributes: 26 | label: Steps to Reproduce 27 | description: Please be as detailed as possible when providing steps to reproduce, failure to provide steps will result in this issue being closed. 28 | validations: 29 | required: true 30 | 31 | - type: textarea 32 | attributes: 33 | label: Extra informatio that you want to share. 34 | description: Any relevant information that you think can be interesting to know related this issue. 35 | validations: 36 | required: false 37 | 38 | - type: checkboxes 39 | attributes: 40 | label: Is there an existing issue for this? 41 | description: Please [search here](https://github.com/ass-a-service/ajobot/issues) to see if an issue already exists for your problem. 42 | options: 43 | - label: I have searched the existing issues before opening this issue. 44 | required: true 45 | - label: I have provided all relevant details, including the specific game and Docker images I am using if this issue is related to running a server. 46 | required: true 47 | - label: I have checked in the Discord server and believe this is a bug with the software, and not a configuration issue with my specific system. 48 | required: true -------------------------------------------------------------------------------- /src/lua/ajo.lua: -------------------------------------------------------------------------------- 1 | --! file: vampire.lua 2 | local strm_key = KEYS[1] 3 | local lb_key = KEYS[2] 4 | local ajo_gain_key = KEYS[3] 5 | local vampire_key = KEYS[4] 6 | local name_key = KEYS[5] 7 | local bomb_key = KEYS[6] 8 | 9 | local id = ARGV[1] 10 | local called_name = ARGV[2] 11 | local event_version = ARGV[3] 12 | local guild_id = ARGV[4] 13 | 14 | -- avoid infinite ajo spam, if the vampire is that high, we quit 15 | local level = tonumber(redis.call("get", vampire_key)) 16 | if level and level > 69 then 17 | return {"err", false} 18 | end 19 | 20 | -- check for a potential bomb 21 | local bomb = redis.call("get", bomb_key) 22 | if bomb then 23 | redis.call("del", bomb_key) 24 | 25 | -- if the user has nothing, nothing happens 26 | local current = tonumber(redis.call("zscore", lb_key, id)) 27 | if not current or current == 0 then 28 | return {"bomb", {bomb, 0}} 29 | end 30 | 31 | -- set off the bomb, apply damage 32 | local pct_dmg = 18 33 | local dmg = math.floor(current * (pct_dmg / 100)) 34 | redis.call("zincrby", lb_key, -dmg, id) 35 | redis.call( 36 | "xadd", strm_key, "*", 37 | "version", event_version, 38 | "type", "bomb", 39 | "user_id", id, 40 | "guild_id", guild_id, 41 | "amount", -dmg 42 | ) 43 | return {"bomb", {bomb, dmg}} 44 | end 45 | 46 | -- check our effects for ajo efficiency 47 | local ajo_gain = tonumber(redis.call("get", ajo_gain_key)) 48 | if not ajo_gain then 49 | ajo_gain = 1 50 | end 51 | 52 | -- add the ajos and update the stream 53 | if ajo_gain ~= 0 then 54 | redis.call("zincrby", lb_key, ajo_gain, id) 55 | redis.call( 56 | "xadd", strm_key, "*", 57 | "version", event_version, 58 | "type", "farm", 59 | "user_id", id, 60 | "guild_id", guild_id, 61 | "amount", ajo_gain 62 | ) 63 | 64 | -- update our username if it has changed 65 | local current_name = redis.call("get", name_key) 66 | if called_name ~= current_name then 67 | redis.call("set", name_key, called_name) 68 | end 69 | 70 | return {"OK", ajo_gain} 71 | end 72 | 73 | return {"OK", 0} 74 | -------------------------------------------------------------------------------- /src/lua/trade.lua: -------------------------------------------------------------------------------- 1 | --! file: trade.lua 2 | local strm_key = KEYS[1] 3 | local source_inv_key = KEYS[2] 4 | local target_inv_key = KEYS[3] 5 | local item_key = KEYS[4] 6 | 7 | local source_id = ARGV[1] 8 | local target_id = ARGV[2] 9 | local item = ARGV[3] 10 | local quantity = math.ceil(tonumber(ARGV[4])) 11 | local event_version = ARGV[5] 12 | local guild_id = ARGV[6] 13 | 14 | -- if the item to trade is unknown, quit 15 | local item_data = redis.call("hmget", item_key, "max_stack", "tradable") 16 | if not item_data then 17 | return {"unknown", false} 18 | end 19 | 20 | local max_stack = tonumber(item_data[1]) 21 | local tradable = tonumber(item_data[2]) 22 | if tradable ~= 1 then 23 | return {"tradable", false} 24 | end 25 | 26 | -- sanity checks 27 | if quantity < 1 then 28 | return {"err", false} 29 | end 30 | 31 | if source_id == target_id then 32 | return {"futile", false} 33 | end 34 | 35 | -- ensure we actually own the item 36 | local source_stack = tonumber(redis.call("hget", source_inv_key, item)) 37 | if not source_stack or source_stack < 1 then 38 | return {"err", false} 39 | end 40 | 41 | -- ensure we have enough items to trade 42 | if source_stack < quantity then 43 | return {"funds", source_stack} 44 | end 45 | 46 | -- verify the target's max stack, max increase is minimised by stack 47 | local incr_quantity 48 | local target_stack = tonumber(redis.call("hget", target_inv_key, item)) 49 | if not target_stack or target_stack == 0 then 50 | incr_quantity = quantity 51 | elseif target_stack > 0 then 52 | incr_quantity = math.min(max_stack - target_stack, quantity) 53 | end 54 | 55 | -- decrease source stack, increase target stack, pass it to stream 56 | redis.call("hincrby", source_inv_key, item, -quantity) 57 | redis.call( 58 | "xadd", strm_key, "*", 59 | "version", event_version, 60 | "type", "trader", 61 | "user_id", source_id, 62 | "guild_id", guild_id, 63 | "item", item, 64 | "quantity", -quantity 65 | ) 66 | if incr_quantity > 0 then 67 | redis.call("hincrby", target_inv_key, item, incr_quantity) 68 | redis.call( 69 | "xadd", strm_key, "*", 70 | "version", event_version, 71 | "type", "tradee", 72 | "user_id", target_id, 73 | "guild_id", guild_id, 74 | "item", item, 75 | "quantity", incr_quantity 76 | ) 77 | end 78 | 79 | return {"OK", true} 80 | -------------------------------------------------------------------------------- /src/lua/discombobulate.lua: -------------------------------------------------------------------------------- 1 | --! discombobulate.lua 2 | local strm_key = KEYS[1] 3 | local lb_key = KEYS[2] 4 | local exp_key = KEYS[3] 5 | local buff_key = KEYS[4] 6 | 7 | local source_id = ARGV[1] 8 | local target_id = ARGV[2] 9 | local offer = math.ceil(tonumber(ARGV[3])) 10 | local event_version = ARGV[4] 11 | local guild_id = ARGV[5] 12 | 13 | redis.replicate_commands() 14 | 15 | -- sanity checks 16 | if offer < 1 then 17 | return {"err", false} 18 | end 19 | 20 | if source_id == target_id then 21 | return {"futile", false} 22 | end 23 | 24 | -- can we discombobulate? 25 | local ttl = tonumber(redis.call("ttl", exp_key)) 26 | if ttl > 0 then 27 | return {"ttl", ttl} 28 | end 29 | 30 | -- can we discombobulate that much? 31 | local source_amount = tonumber(redis.call("zscore", lb_key, source_id)) 32 | if not source_amount or source_amount < offer then 33 | return {"funds", false} 34 | end 35 | 36 | -- minimum offer is 35% of the victim 37 | local target_amount = tonumber(redis.call("zscore", lb_key, target_id)) 38 | 39 | -- fix corner case when victim doesn't have ajos because it hasn't played 40 | -- (this happened in dev with a fresh environment but I guess it 41 | -- could happen elsewhere) 42 | if not target_amount then 43 | target_amount = 0 44 | end 45 | local min_offer = math.ceil((35 / 100) * target_amount) 46 | if not target_amount or offer < min_offer then 47 | return {"offer", min_offer} 48 | end 49 | 50 | -- dmg is 69 to 200% of offered, but can be buffed 51 | local min = 69 52 | local max = 200 53 | local percent = math.random(min, max) 54 | local ttl = percent * 1800 55 | 56 | -- the damage percent may be buffed (but not its expiration) 57 | local buff = tonumber(redis.call("get", buff_key)) 58 | if buff and buff > 0 then 59 | percent = percent + buff 60 | end 61 | 62 | local dmg = math.floor(percent / 100 * offer) 63 | if target_amount - dmg < 0 then 64 | dmg = target_amount 65 | end 66 | 67 | -- lock for percentage done in hours 68 | redis.call("zincrby", lb_key, -offer, source_id) 69 | redis.call("zincrby", lb_key, -dmg, target_id) 70 | redis.call("set", exp_key, 1, "ex", ttl) 71 | 72 | -- append data to stream 73 | redis.call( 74 | "xadd", strm_key, "*", 75 | "version", event_version, 76 | "type", "discombobulator", 77 | "user_id", source_id, 78 | "discombobulatee_id", target_id, 79 | "guild_id", guild_id, 80 | "amount", -offer 81 | ) 82 | redis.call( 83 | "xadd", strm_key, "*", 84 | "version", event_version, 85 | "type", "discombobulatee", 86 | "user_id", target_id, 87 | "discombobulator_id", source_id, 88 | "guild_id", guild_id, 89 | "amount", -dmg 90 | ) 91 | return {"OK", dmg} 92 | -------------------------------------------------------------------------------- /src/exts/general.py: -------------------------------------------------------------------------------- 1 | from disnake import CommandInteraction 2 | from disnake.ext.commands import Cog, slash_command 3 | from os import environ 4 | 5 | from src.impl.bot import Bot 6 | 7 | class General(Cog): 8 | def __init__(self, bot: Bot) -> None: 9 | self.bot = bot 10 | 11 | @slash_command(name="support", description="Get an invite link to the support server.") 12 | async def support(self, itr: CommandInteraction) -> None: 13 | await itr.send("Here's an invite to my support server: /dev/null", ephemeral=True) 14 | 15 | @slash_command(name="dame", description="Give movidas.") 16 | async def dame(self, itr: CommandInteraction, type: str, amount: int) -> None: 17 | if not "DEBUG" in environ or environ["DEBUG"] != "1": 18 | return await itr.send("mongo") 19 | 20 | match type: 21 | case "ajos": 22 | await self.bot.manager.redis.zadd("lb", {itr.author.id: amount}) 23 | return await itr.send(f"Set ajos to {amount}") 24 | case "chop"|"bomb"|"cross"|"ribb"|"herb"|"sauro"|"eggplant"|"shoe"|"tooth"|"bone"|"magic_wand"|"gear"|"satellite": 25 | inv_key = f"{itr.author.id}:inventory" 26 | it_type = self.__trans(type) 27 | if not it_type: 28 | return await itr.send(f"unk item {type}") 29 | 30 | await self.bot.manager.redis.hset(inv_key, it_type, amount) 31 | return await itr.send(f"Set {it_type} to {amount}") 32 | case "vampire": 33 | vam_key = f"{itr.author.id}:vampire" 34 | await self.bot.manager.redis.set(vam_key, amount) 35 | return await itr.send(f"Set vampire to {amount}") 36 | 37 | await itr.send("Nothing") 38 | 39 | def __trans(self, it_type: str) -> str: 40 | match it_type: 41 | case "chop": 42 | return ":chopsticks:" 43 | case "bomb": 44 | return ":bomb:" 45 | case "cross": 46 | return ":cross:" 47 | case "ribb": 48 | return ":reminder_ribbon:" 49 | case "herb": 50 | return ":herb:" 51 | case "sauro": 52 | return ":sauro:" 53 | case "eggplant": 54 | return ":eggplant:" 55 | case "shoe": 56 | return ":athletic_shoe:" 57 | case "tooth": 58 | return ":tooth:" 59 | case "bone": 60 | return ":bone:" 61 | case "magic_wand": 62 | return ":magic_wand:" 63 | case "gear": 64 | return ":gear:" 65 | case "satellite": 66 | return ":satellite:" 67 | 68 | return ":void:" 69 | 70 | 71 | def setup(bot: Bot) -> None: 72 | bot.add_cog(General(bot)) 73 | -------------------------------------------------------------------------------- /src/lua/craft.lua: -------------------------------------------------------------------------------- 1 | --! file: craft.lua 2 | local ajo_strm_key = KEYS[1] 3 | local inv_strm_key = KEYS[2] 4 | local lb_key = KEYS[3] 5 | local inventory_key = KEYS[4] 6 | local item_key = KEYS[5] 7 | local craft_key = KEYS[6] 8 | 9 | local item = ARGV[1] 10 | local user_id = ARGV[2] 11 | local event_version = ARGV[3] 12 | local guild_id = ARGV[4] 13 | 14 | -- ensure we have not reached the max stack yet 15 | local stack = tonumber(redis.call("hget", inventory_key, item)) 16 | if not stack then 17 | stack = 0 18 | end 19 | 20 | local max_stack = tonumber(redis.call("hget", item_key, "max_stack")) 21 | if not max_stack then 22 | return {"unknown", false} 23 | elseif stack >= max_stack then 24 | return {"stack", false} 25 | end 26 | 27 | -- retrieve item data, check if we can pay for everything 28 | local item_data = redis.call("lrange", craft_key, 0, -1) 29 | if not item_data then 30 | return {"unknown", false} 31 | end 32 | 33 | local size = #item_data 34 | if size < 1 then 35 | return {"unknown", false} 36 | end 37 | 38 | -- assumes that it is not possible to have the same currency multiple times 39 | local index = 1 40 | local currency, price, funds 41 | while index < size do 42 | currency = item_data[index] 43 | price = tonumber(item_data[index + 1]) 44 | index = index + 2 45 | 46 | -- all currencies are from inventory apart from garlics 47 | if currency == ":garlic:" then 48 | funds = tonumber(redis.call("zscore", lb_key, user_id)) 49 | else 50 | funds = tonumber(redis.call("hget", inventory_key, currency)) 51 | end 52 | 53 | if not funds or funds < price then 54 | return {"funds", {currency, price}} 55 | end 56 | end 57 | 58 | -- now that we know we can pay, decrease all currencies 59 | index = 1 60 | while index < size do 61 | currency = item_data[index] 62 | price = tonumber(item_data[index + 1]) 63 | index = index + 2 64 | 65 | if currency == ":garlic:" then 66 | redis.call("zincrby", lb_key, -price, user_id) 67 | redis.call( 68 | "xadd", ajo_strm_key, "*", 69 | "version", event_version, 70 | "type", "craft_fee", 71 | "user_id", user_id, 72 | "guild_id", guild_id, 73 | "item", item, 74 | "amount", -price 75 | ) 76 | else 77 | redis.call("hincrby", inventory_key, currency, -price) 78 | redis.call( 79 | "xadd", inv_strm_key, "*", 80 | "version", event_version, 81 | "type", "craft_fee", 82 | "user_id", user_id, 83 | "guild_id", guild_id, 84 | "item", item, 85 | "quantity", -price 86 | ) 87 | end 88 | end 89 | 90 | -- finally craft the item 91 | stack = redis.call("hincrby", inventory_key, item, 1) 92 | redis.call( 93 | "xadd", inv_strm_key, "*", 94 | "version", event_version, 95 | "type", "item_crafted", 96 | "user_id", user_id, 97 | "guild_id", guild_id, 98 | "item", item, 99 | "quantity", 1 100 | ) 101 | return {"OK", {item, stack}} 102 | -------------------------------------------------------------------------------- /src/lua/vampire.lua: -------------------------------------------------------------------------------- 1 | --! file: vampire.lua 2 | local strm_key = KEYS[1] 3 | local lb_key = KEYS[2] 4 | local vampire_key = KEYS[3] 5 | local curse_key = KEYS[4] 6 | local inventory_key = KEYS[5] 7 | local deb_key = KEYS[6] 8 | 9 | local id = ARGV[1] 10 | local event_version = ARGV[2] 11 | local guild_id = ARGV[3] 12 | 13 | redis.replicate_commands() 14 | 15 | -- grab the vampire level that would appear (not current) 16 | local level = tonumber(redis.call("get", vampire_key)) 17 | if not level then 18 | level = 1 19 | end 20 | 21 | -- calculate appear chance 22 | local appear_chance 23 | if level == 1 then 24 | appear_chance = 1 25 | else 26 | appear_chance = math.log10(level) * 20 27 | end 28 | 29 | -- if the user is cursed, the appear chance is increased 30 | local curse = tonumber(redis.call("get", curse_key)) 31 | if curse and curse > 0 then 32 | appear_chance = appear_chance + curse 33 | end 34 | 35 | -- quit if the vampire does not appear 36 | local rand = math.random(0, 99) 37 | 38 | -- DEBUG the random 39 | redis.call( 40 | "xadd", deb_key, "*", 41 | "version", event_version, 42 | "type", "debug_vampire", 43 | "user_id", id, 44 | "guild_id", guild_id, 45 | "random", rand, 46 | "appear_chance", appear_chance 47 | ) 48 | 49 | if appear_chance < rand then 50 | return {"OK", false} 51 | end 52 | 53 | -- determine damage of the vampire and its ttl 54 | local ttl_per_level = 600 55 | local current = tonumber(redis.call("zscore", lb_key, id)) 56 | local ttl = math.min(ttl_per_level * level, 7200) 57 | 58 | -- if we don't have any ajos, refresh the vampire's current TTL but do not damage 59 | -- this is here to mess with spammers 60 | if not current or current < 1 then 61 | redis.call("expire", vampire_key, ttl) 62 | return {"OK", false} 63 | end 64 | 65 | -- calculate damage 66 | local dmg, min, max, pct_dmg, op_result 67 | 68 | -- is it protected with a necklace? 69 | local item = ":reminder_ribbon:" 70 | local has_necklace = redis.call("hget", inventory_key, item) 71 | if has_necklace and tonumber(has_necklace) >= 1 then 72 | dmg = 0 73 | -- Remove the necklace by 1, add to stream 74 | redis.call("hincrby", inventory_key, item, -1) 75 | redis.call( 76 | "xadd", strm_key, "*", 77 | "version", event_version, 78 | "type", "item_used", 79 | "user_id", id, 80 | "guild_id", guild_id, 81 | "item", item, 82 | "quantity", -1 83 | ) 84 | op_result = "NECKLACE" 85 | else 86 | min = math.min(level * 1.2, 30) 87 | max = math.min(40, level * 2) 88 | pct_dmg = math.random(min, max) 89 | dmg = math.ceil(current * (pct_dmg / 100)) 90 | op_result = "OK" 91 | end 92 | 93 | -- increase the vampire's level, refresh his ttl 94 | redis.call("set", vampire_key, level + 1, "EX", ttl) 95 | 96 | -- apply damage, add to stream 97 | redis.call("zincrby", lb_key, -dmg, id) 98 | redis.call( 99 | "xadd", strm_key, "*", 100 | "version", event_version, 101 | "type", "vampire", 102 | "user_id", id, 103 | "guild_id", guild_id, 104 | "amount", -dmg 105 | ) 106 | return {op_result, {level, dmg}} 107 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 105 | __pypackages__/ 106 | 107 | # Celery stuff 108 | celerybeat-schedule 109 | celerybeat.pid 110 | 111 | # SageMath parsed files 112 | *.sage.py 113 | 114 | # Environments 115 | .env 116 | .venv 117 | env/ 118 | venv/ 119 | ENV/ 120 | env.bak/ 121 | venv.bak/ 122 | 123 | # Spyder project settings 124 | .spyderproject 125 | .spyproject 126 | 127 | # Rope project settings 128 | .ropeproject 129 | 130 | # mkdocs documentation 131 | /site 132 | 133 | # mypy 134 | .mypy_cache/ 135 | .dmypy.json 136 | dmypy.json 137 | 138 | # Pyre type checker 139 | .pyre/ 140 | 141 | # pytype static type analyzer 142 | .pytype/ 143 | 144 | # Cython debug symbols 145 | cython_debug/ 146 | 147 | # PyCharm 148 | # JetBrains specific template is maintainted in a separate JetBrains.gitignore that can 149 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 150 | # and can be added to the global gitignore or merged into this file. For a more nuclear 151 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 152 | #.idea/ 153 | redis_data/ -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | When contributing to this repository, please first discuss the change you wish to make via issue, 4 | email, or any other method with the owners of this repository before making a change. 5 | 6 | Please note we have a code of conduct, please follow it in all your interactions with the project. 7 | 8 | ## Development dependencies 9 | 10 | - Docker 11 | - Docker compose 12 | - pyenv (optional) 13 | - Python 3.10 14 | - poetry (optional) 15 | - Discord account with [development portal](https://discord.com/developers/) 16 | 17 | ## Pull Request Process 18 | 19 | 1. Ensure any install or build dependencies are removed before the end of the layer when doing a 20 | build. 21 | 2. Update the README.md and ARCHITECTURE.md documents with details of changes to the interface, 22 | this includes new environment variables, exposed ports, useful file locations and 23 | container parameters. 24 | 3. You may merge the Pull Request in once you have the sign-off of one maintainer. 25 | 26 | ## Code of Conduct 27 | 28 | ### Our Pledge 29 | 30 | In the interest of fostering an open and welcoming environment, we as 31 | contributors and maintainers pledge to making participation in our project and 32 | our community a harassment-free experience for everyone, regardless of age, body 33 | size, disability, ethnicity, gender identity and expression, level of experience, 34 | nationality, personal appearance, race, religion, or sexual identity and 35 | orientation. 36 | 37 | ### Our Standards 38 | 39 | Examples of behavior that contributes to creating a positive environment 40 | include: 41 | 42 | * Using welcoming and inclusive language 43 | * Being respectful of differing viewpoints and experiences 44 | * Gracefully accepting constructive criticism 45 | * Focusing on what is best for the community 46 | * Showing empathy towards other community members 47 | 48 | Examples of unacceptable behavior by participants include: 49 | 50 | * The use of sexualized language or imagery and unwelcome sexual attention or 51 | advances 52 | * Trolling, insulting/derogatory comments, and personal or political attacks 53 | * Public or private harassment 54 | * Publishing others' private information, such as a physical or electronic 55 | address, without explicit permission 56 | * Other conduct which could reasonably be considered inappropriate in a 57 | professional setting 58 | 59 | ### Our Responsibilities 60 | 61 | Project maintainers are responsible for clarifying the standards of acceptable 62 | behavior and are expected to take appropriate and fair corrective action in 63 | response to any instances of unacceptable behavior. 64 | 65 | Project maintainers have the right and responsibility to remove, edit, or 66 | reject comments, commits, code, wiki edits, issues, and other contributions 67 | that are not aligned to this Code of Conduct, or to ban temporarily or 68 | permanently any contributor for other behaviors that they deem inappropriate, 69 | threatening, offensive, or harmful. 70 | 71 | ### Scope 72 | 73 | This Code of Conduct applies both within project spaces and in public spaces 74 | when an individual is representing the project or its community. Examples of 75 | representing a project or community include using an official project e-mail 76 | address, posting via an official social media account, or acting as an appointed 77 | representative at an online or offline event. Representation of a project may be 78 | further defined and clarified by project maintainers. 79 | 80 | ### Enforcement 81 | 82 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 83 | reported by contacting the project team at [INSERT EMAIL ADDRESS]. All 84 | complaints will be reviewed and investigated and will result in a response that 85 | is deemed necessary and appropriate to the circumstances. The project team is 86 | obligated to maintain confidentiality with regard to the reporter of an incident. 87 | Further details of specific enforcement policies may be posted separately. 88 | 89 | Project maintainers who do not follow or enforce the Code of Conduct in good 90 | faith may face temporary or permanent repercussions as determined by other 91 | members of the project's leadership. 92 | 93 | ### Attribution 94 | 95 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 96 | available at [http://contributor-covenant.org/version/1/4][version] 97 | 98 | [homepage]: http://contributor-covenant.org 99 | [version]: http://contributor-covenant.org/version/1/4/ 100 | -------------------------------------------------------------------------------- /bootstrap_items.py: -------------------------------------------------------------------------------- 1 | # this file is used to generate redis protocol in order to bulk load the 2 | # item data (max stack, currency, prices, chance to appear) 3 | def proto(cmd, key, data) -> None: 4 | # stringify the data 5 | strings = [cmd, key] 6 | for value in data: 7 | strings.append(value) 8 | 9 | # transform to redis proto 10 | arg_count = len(strings) 11 | parts = [f"*{arg_count}"] 12 | for s in strings: 13 | parts.extend([f"${len(s)}", s]) 14 | 15 | # clear up the key 16 | sz = len(key) 17 | print(f"*2\r\n$3\r\nDEL\r\n${sz}\r\n{key}", end="\r\n") 18 | print("\r\n".join(parts), end="\r\n") 19 | 20 | def main() -> None: 21 | data = { 22 | ":sauropod:": { 23 | "description": "It's a sauropod.", 24 | "drop_rate": 1, 25 | "tradable": 1, 26 | "max_stack": 1 27 | }, 28 | ":chopsticks:": { 29 | "description": "Efficient weapon against vampires.", 30 | "drop_rate": 250, 31 | "tradable": 1, 32 | "max_stack": 5 33 | }, 34 | ":cross:": { 35 | "description": "Helps you fight vampires.", 36 | "drop_rate": 500, 37 | "tradable": 1, 38 | "max_stack": 10, 39 | "craft": { 40 | ":herb:": 4 41 | } 42 | }, 43 | ":bomb:": { 44 | "description": "Plants a bomb, better not be there when it explodes.", 45 | "drop_rate": 900, 46 | "tradable": 1, 47 | "max_stack": 1 48 | }, 49 | ":herb:": { 50 | "description": "Just some herbs.", 51 | "drop_rate": 4000, 52 | "tradable": 1, 53 | "max_stack": 20 54 | }, 55 | ":reminder_ribbon:": { 56 | "description": "Protects you against vampires.", 57 | "tradable": 1, 58 | "max_stack": 1, 59 | "craft": { 60 | ":garlic:": 50 61 | } 62 | }, 63 | ":athletic_shoe:": { 64 | "description": "Makes you run faster through the ajo fields.", 65 | "drop_rate": 300, 66 | "tradable": 1, 67 | "ttl": 600, 68 | "max_stack": 1 69 | }, 70 | ":eggplant:": { 71 | "description": "Makes you stronger.", 72 | "drop_rate": 300, 73 | "tradable": 1, 74 | "ttl": 600, 75 | "buff": 31, 76 | "max_stack": 1 77 | }, 78 | ":bone:": { 79 | "description": "A human bone.", 80 | "drop_rate": 2500, 81 | "tradable": 1, 82 | "max_stack": 206 83 | }, 84 | ":tooth:": { 85 | "description": "A human tooth.", 86 | "drop_rate": 2000, 87 | "tradable": 1, 88 | "max_stack": 32 89 | }, 90 | ":magic_wand:": { 91 | "description": "Curse your foes.", 92 | "ttl": 7200, 93 | "curse": 12, 94 | "max_stack": 1, 95 | "craft": { 96 | ":bone:": 5, 97 | ":tooth:": 2 98 | } 99 | }, 100 | ":satellite:": { 101 | "description": "Enhance your vision.", 102 | "tradable": 1, 103 | "max_stack": 4, 104 | "craft": { 105 | ":gear:": 3 106 | } 107 | }, 108 | ":gear:": { 109 | "description": "A gear.", 110 | "drop_rate": 600, 111 | "tradable": 1, 112 | "max_stack": 20, 113 | "craft": { 114 | ":garlic:": 15 115 | } 116 | } 117 | } 118 | 119 | drop_rate = [] 120 | for item, item_data in data.items(): 121 | # prepare drop rate hash 122 | if "drop_rate" in item_data: 123 | drop_rate.extend([item, str(item_data["drop_rate"]), str(item_data["max_stack"])]) 124 | 125 | # prototype the item hash, note that we use a list for simplicity 126 | hitem = [] 127 | for key, value in item_data.items(): 128 | if key != "craft": 129 | hitem.extend([key, str(value)]) 130 | proto("HSET", "items:{}".format(item), hitem) 131 | 132 | # prototype the craft data 133 | if "craft" in item_data: 134 | craft_data = [] 135 | for key, value in item_data["craft"].items(): 136 | craft_data.extend([key, str(value)]) 137 | proto("RPUSH", "craft:{}".format(item), craft_data) 138 | 139 | # prototype drop rate, but clear it first 140 | proto("RPUSH", "drop-rate", drop_rate) 141 | 142 | main() 143 | -------------------------------------------------------------------------------- /ARCHITECTURE.md: -------------------------------------------------------------------------------- 1 | # ARCHITECTURE 2 | 3 | ## Redis data structure 4 | ### Key list 5 | | Type | Format | Description | 6 | |------------|----------------------------|-------------| 7 | | sorted set | `lb` | ajo leaderboard | 8 | | stream | `ajobus` | ajo changes | 9 | | stream | `ajobus-inventory` | inventory changes | 10 | | sorted set | `ajocron-bomb` | bomb cron | 11 | | string | `{id}` | user human id | 12 | | hash | `{id}:inventory` | user inventory | 13 | | string | `{id}:daily` | user daily reward expiration | 14 | | string | `{id}:weekly` | user daily reward expiration | 15 | | string | `{id}:vampire` | user next vampire level | 16 | | string | `{id}:ajo-gain` | user ajo farm efficiency | 17 | | string | `{id}:discombobulate-buff` | user discombobulate buff | 18 | | string | `{id}:wand-curse` | user wand curse | 19 | | hash | `items:{item}` | item data (bootstrapped) | 20 | | list | `craft:{item}` | item craft data (bootstrapped) | 21 | | list | `drop-rate` | item drop rate (bootstrapped) | 22 | | string | `ajobomb` | bomb flag | 23 | 24 | ### Identifier 25 | The ajo-bot uses discord's user ID, a redis key holds its discord username. 26 | This key is checked and changed if necessary on each operation. 27 | 28 | ``` 29 | > set 111 "Zymna#0001" 30 | > get 111 31 | "Zymna#0001" 32 | > set 222 "Axl#0001" 33 | ``` 34 | 35 | ### Leaderboard 36 | The leaderboard is a redis sorted set connecting a user ID to his score. 37 | 38 | ``` 39 | # [ 40 | # {name: "111", count: 7} 41 | # {name: "222", count: 4} 42 | # ] 43 | # leaderboard implemented as a sorted set 44 | > zincrby lb 7 "111" 45 | > zincrby lb 4 "222" 46 | ``` 47 | 48 | ### Drop rate 49 | This list contains both items drop rate and their max stack. 50 | It is loaded at bootstrap in redis. 51 | 52 | Note: in practice, we need the farm script to know both the drop rate and the 53 | max stack possible. The list is here to avoid passing all the item keys to the 54 | LUA script since we should not access dynamically generated keys from LUA. 55 | 56 | ``` 57 | # example setup 58 | > del drop-rate 59 | > rpush drop-rate ":cross:" 500 10 60 | > rpush drop-rate ":herb:" 1000 20 61 | ``` 62 | 63 | ### Item data 64 | This hash contains information on items, currently the only worthy information 65 | is the maximum stack. It is loaded at bootstrap in redis. 66 | 67 | Note: since the identifier of some items is a discord emoji, the `:` separation 68 | standard is not respected. 69 | 70 | ``` 71 | # example setup 72 | > del items::cross: 73 | > hset items::cross: max_stack 10 currency ":herb:" price 4 74 | > hset items::reminder_ribbon: max_stack 10 currency ":garlic:" price 50 75 | > hset items::chopsticks: max_stack 1 76 | ``` 77 | 78 | ### Craft data 79 | This list contains each currencies and the price required to craft the item. 80 | It is loaded at bootstrap in redis. 81 | 82 | ``` 83 | # example setup 84 | > del craft::cross: 85 | > rpush craft::cross: ":garlic:" 50 86 | > rpush craft::reminder_ribbon: ":herb:" 4 87 | ``` 88 | 89 | ### Bomb 90 | Each user's bombs are inserted into a sorted set with the score being the 91 | timestamp at which it should explode. A cron job periodically cleans the sorted 92 | set and sets the bomb flag to the owner's name. 93 | 94 | ``` 95 | # bomb setup 96 | > zadd ajocron-bomb 1657155477 111 97 | # cron retrieve 98 | > zrangebyscore ajocron-bomb -inf {time} 99 | > zremrangebyscore ajocron-bomb -inf {time} 100 | ``` 101 | 102 | ### User keys 103 | All user related keys must follow the following schema: `{id}:{key}`. If the key 104 | is composed of multiple words, a hyphen `-` should be used for separation. 105 | 106 | #### Expiring rewards 107 | Time based rewards are stored in an expiring redis key. The key is checked when 108 | rewarding, and set to a value of 1 with the related TTL when the reward applies. 109 | 110 | ``` 111 | > set 111:daily 1 ex 86400 # daily ttl 112 | > set 111:weekly 1 ex 604800 # weekly ttl 113 | ``` 114 | 115 | #### Vampire 116 | The vampire key has a value equal to the next level of vampire that will appear. 117 | The key is changed to expire according to the vampire level. 118 | 119 | ``` 120 | # a vampire level 1 will appear, and expires in 10 minutes 121 | > set 111:vampire 1 EX 600 122 | ``` 123 | 124 | #### Inventory 125 | The user's inventory is stored into a hash. 126 | ``` 127 | > hincrby 111:inventory ":cross:" 1 128 | > hgetall 111:inventory 129 | ``` 130 | 131 | ## Event streaming 132 | All write operations are recorded into two different redis streams: 133 | * the stream of ajo changes: `ajobus` 134 | * the stream of inventory changes: `ajobus-inventory` 135 | 136 | All events include a type describing where the change comes from: 137 | ``` 138 | # earned an item 139 | > xadd "ajobus-inventory" "*" "user_id" 111 "item" ":cross:" "quantity" 1 "type" "item_earned" 140 | 141 | # traded an item 142 | > xadd "ajobus-inventory" "*" "user_id" 111 "item" ":cross:" "quantity" -1 "type" "trader" 143 | > xadd "ajobus-inventory" "*" "user_id" 222 "item" ":cross:" "quantity" 1 "type" "tradee" 144 | 145 | # earned ajos 146 | > xadd "ajobus" "*" "user_id" 111 "amount" 1 "type" "farm" 147 | 148 | # gamble 149 | > xadd "ajobus" "*" "user_id" 111 "amount" 20 "type" "gamble" 150 | 151 | # discombobulate 152 | > xadd "ajobus" "*" "user_id" 111 "amount" -20 "type" "discombobulator" 153 | > xadd "ajobus" "*" "user_id" 111 "amount" -25 "type" "discombobulatee" 154 | ``` 155 | 156 | ## Operation examples 157 | Most operations run through a LUA script to avoid concurrency. 158 | 159 | ### `pay.lua` 160 | ``` 161 | # 222 pays 4 to 111, with count check: 162 | # ensure the user can pay 163 | > zscore lb 222 164 | # update leaderboard 165 | > zincrby lb -4 222 166 | > zincrby lb 4 111 167 | ``` 168 | 169 | ### `timely-reward` 170 | ``` 171 | # Axl#0001 daily award 172 | # ensure the user can be rewarded 173 | > ttl 222:daily 174 | # reward and block until next 175 | > zincrby lb 32 222 176 | > set 222 1 EX 86400 177 | ``` 178 | 179 | ### Event guidelines 180 | Events MUST include the following attributes at least: 181 | * `version` the version of the event 182 | * `type` the type of the event 183 | * `user_id` the user identifier to which the event applied 184 | * `guild_id` the guild identifier where the event applied (0 if none) 185 | 186 | ### LUA guidelines 187 | The priority of keys for LUA scripts is the following: 188 | * first the stream keys 189 | * next any other key which is required 190 | 191 | The priority for arguments of LUA scripts is the following: 192 | * first the arguments required for the operation 193 | * next the arguments required for events 194 | * last the seed used for random if any 195 | -------------------------------------------------------------------------------- /src/exts/ajo.py: -------------------------------------------------------------------------------- 1 | from disnake import CommandInteraction, Message, User, Guild, Embed 2 | from disnake.ext.commands import Cog, Context, Param, command, slash_command 3 | from disnake.ext import tasks 4 | from redis.exceptions import ResponseError 5 | from loguru import logger 6 | 7 | from src.impl.bot import Bot 8 | import time 9 | from os import environ 10 | 11 | AJO = "🧄" 12 | LEADERBOARD = "lb" 13 | EVENT_VERSION = 1 14 | AJOBUS = "ajobus" 15 | AJOBUS_INVENTORY = "ajobus-inventory" 16 | 17 | class Ajo(Cog): 18 | def __init__(self, bot: Bot) -> None: 19 | self.bot = bot 20 | self.on_ajo.start() 21 | self.bomb_cron.start() 22 | 23 | @tasks.loop(seconds=10) 24 | async def bomb_cron(self) -> None: 25 | try: 26 | redis = self.bot.manager.redis 27 | 28 | # read keys not read yet, assume these are bombs 29 | tm = time.time() 30 | data = await redis.zrangebyscore("ajocron-bomb", "-inf", tm) 31 | 32 | # we only care about the last item, bombs at the same time overwrite 33 | if len(data): 34 | # setup the bomb flag with the related username 35 | user_id = data[-1] 36 | await redis.set("ajobomb", await redis.get(user_id)) 37 | 38 | # cleanup the cron 39 | await redis.zremrangebyscore("ajocron-bomb", "-inf", tm) 40 | except: 41 | logger.error("There was an exception while running bomb_cron task. Retrying.") 42 | 43 | @tasks.loop(seconds=1) 44 | async def on_ajo(self) -> None: 45 | try: 46 | redis = self.bot.manager.redis 47 | 48 | # Create the xreadgroup once 49 | # TODO: better do this outside of this fn 50 | try: 51 | await redis.xgroup_create(AJOBUS,"ajo-python",0, mkstream=True) 52 | except ResponseError as e: 53 | if str(e) != "BUSYGROUP Consumer Group name already exists": 54 | raise e 55 | 56 | data = await redis.xreadgroup("ajo-python","ajo.py",streams={AJOBUS: ">"},count=100) 57 | # stream_name, chunk 58 | for _, chunk in data: 59 | # entry_id, entry_data 60 | for entry_id, entry_data in chunk: 61 | entry = await self.parseEntry(entry_data) 62 | if entry["type"] == "farm": 63 | user_id = entry["user_id"] 64 | await redis.evalsha( 65 | environ['farm_inventory'], 66 | 4, 67 | AJOBUS_INVENTORY, 68 | "drop-rate", 69 | LEADERBOARD, 70 | user_id + ":inventory", 71 | user_id, 72 | EVENT_VERSION, 73 | entry["guild_id"], 74 | entry_id, 75 | time.time_ns()-(int(time.time())*1000000000) 76 | ) 77 | elif entry["type"] == "discombobulatee": 78 | target = self.bot.get_user(int(entry["user_id"])) 79 | guild = self.bot.get_guild(int(entry["guild_id"])) 80 | discombobulator = self.bot.get_user(int(entry["discombobulator_id"])) 81 | await target.send(f"You have been discombobulated by {discombobulator.name}#{discombobulator.discriminator} from guild "+guild.name) 82 | 83 | 84 | except Exception as e: 85 | logger.error("There was an exception while running on_ajo task. Retrying.") 86 | 87 | 88 | @Cog.listener() 89 | async def on_message(self, message: Message) -> None: 90 | if message.author.bot or message.guild is None: 91 | return 92 | 93 | contains_ajo = await self.bot.manager.contains_ajo(message.content) 94 | 95 | # Relevant message 96 | if contains_ajo: 97 | ajo_gain_key = f"{message.author.id}:ajo-gain" 98 | vampire_key = f"{message.author.id}:vampire" 99 | bomb_key = "ajobomb" 100 | err, res = await self.bot.manager.redis.evalsha( 101 | environ["ajo"], 102 | 6, 103 | AJOBUS, 104 | LEADERBOARD, 105 | ajo_gain_key, 106 | vampire_key, 107 | message.author.id, 108 | bomb_key, 109 | message.author.id, 110 | f"{message.author.name}#{message.author.discriminator}", 111 | EVENT_VERSION, 112 | message.guild.id # farm always has a guild 113 | ) 114 | 115 | match err.decode("utf-8"): 116 | case "err": 117 | return 118 | case "bomb": 119 | bomb_owner = res[0].decode("utf-8") 120 | return await message.reply(f"{bomb_owner}'s bomb explodes! {res[1]} ajos have been burnt.") 121 | 122 | is_begging = await self.bot.manager.is_begging_for_ajo(message.content) 123 | if is_begging: 124 | await message.add_reaction(AJO) 125 | 126 | # util to decode a redis stream entry 127 | async def parseEntry(self, data): 128 | res = {} 129 | for key, value in data.items(): 130 | res[key.decode("utf-8")] = value.decode("utf-8") 131 | return res 132 | 133 | async def getGuildId(self, guild: Guild) -> str: 134 | return 0 if guild is None else str(guild.id) 135 | 136 | # AJO/VERAJO 137 | @command(name="ajo", description="Get your count of ajos.") 138 | async def ajo_command(self, ctx: Context[Bot]) -> None: 139 | count = await self.bot.manager.get_ajo(ctx.author.id) 140 | await ctx.reply(f"{AJO} You have {count} ajos {AJO}") 141 | 142 | @slash_command(name="ajo", description="Get your count of ajos.") 143 | async def ajo(self, itr: CommandInteraction) -> None: 144 | count = await self.bot.manager.get_ajo(itr.author.id) 145 | await itr.send(f"{AJO} You have {count} ajos {AJO}") 146 | 147 | @command(name="verajo", description="See someone else's ajos.") 148 | async def verajo_command(self, ctx: Context[Bot], user: User) -> None: 149 | count = await self.bot.manager.get_ajo(user.id) 150 | await ctx.reply(f"{AJO} {user} has {count} ajos {AJO}") 151 | 152 | @slash_command(name="verajo", description="See someone else's ajos.") 153 | async def verajo( 154 | self, 155 | itr: CommandInteraction, 156 | user: User = Param(description="The user to get the ajo count from.") 157 | ) -> None: 158 | count = await self.bot.manager.get_ajo(user.id) 159 | await itr.send(f"{AJO} {user} has {count} ajos {AJO}") 160 | 161 | async def __get_leaderboard(self) -> Embed: 162 | embed = Embed( 163 | title="Ajo Leaderboard", 164 | colour=0x87CEEB, 165 | ) 166 | lb = await self.bot.manager.get_leaderboard() 167 | j = 0 168 | for name,points in lb.items(): 169 | embed.add_field( 170 | name=f"{j} . {name}", 171 | value=f"{AJO} {points}", 172 | inline=True 173 | ) 174 | j += 1 175 | 176 | return embed 177 | 178 | # LEADERBOARD 179 | @command(name="leaderboard", description="Get the ajo leaderboard.") 180 | async def leaderboard_command(self, ctx: Context[Bot]) -> None: 181 | await ctx.reply(embed=await self.__get_leaderboard()) 182 | 183 | @slash_command(name="leaderboard", description="Get the ajo leaderboard.") 184 | async def leaderboard(self, itr: CommandInteraction) -> None: 185 | await itr.send(embed=await self.__get_leaderboard()) 186 | 187 | # GAMBLE 188 | async def __gamble(self, user: User, amount: str, guild: Guild) -> str: 189 | return await self.bot.manager.gamble_ajo( 190 | user.id, 191 | amount, 192 | await self.getGuildId(guild) 193 | ) 194 | 195 | @command(name="gamble", description="Gamble your ajos.") 196 | async def gamble_command(self, ctx: Context[Bot], amount: str) -> None: 197 | await ctx.reply(await self.__gamble(ctx.author, amount, ctx.guild)) 198 | 199 | @slash_command(name="gamble", description="Gamble your ajos.") 200 | async def gamble( 201 | self, 202 | itr: CommandInteraction, 203 | amount: str = Param(description="How much ajos to gamble.") 204 | ) -> None: 205 | await itr.send(await self.__gamble(itr.author, amount, itr.guild)) 206 | 207 | # PAY 208 | async def __pay( 209 | self, 210 | from_user: User, 211 | to_user: User, 212 | amount: int, 213 | guild: Guild 214 | ) -> str: 215 | reply = await self.bot.manager.pay_ajo( 216 | from_user.id, 217 | to_user.id, 218 | amount, 219 | await self.getGuildId(guild) 220 | ) 221 | return reply.replace("[[TO_USER]]", f"{to_user}") 222 | 223 | 224 | @command(name="pay", description="Pay someone ajos.") 225 | async def pay_command(self, ctx: Context[Bot], user: User, amount: int) -> None: 226 | await ctx.reply(await self.__pay(ctx.author, user, amount, ctx.guild)) 227 | 228 | @slash_command(name="pay", description="Pay someone ajos.") 229 | async def pay( 230 | self, 231 | itr: CommandInteraction, 232 | user: User = Param(description="The user to pay."), 233 | amount: int = Param(description="The amount to pay."), 234 | ) -> None: 235 | await itr.send(await self.__pay(itr.author, user, amount, itr.guild)) 236 | 237 | # WEEKLY CLAIM 238 | async def __weekly(self, user: User, guild: Guild) -> str: 239 | return await self.bot.manager.claim_weekly( 240 | user.id, 241 | await self.getGuildId(guild) 242 | ) 243 | 244 | @command(name="weekly", description="Claim your weekly ajos.") 245 | async def weekly_command(self, ctx: Context[Bot]) -> None: 246 | await ctx.reply(await self.__weekly(ctx.author, ctx.guild)) 247 | 248 | @slash_command(name="weekly", description="Claim your weekly ajos.") 249 | async def weekly(self, itr: CommandInteraction) -> None: 250 | await itr.send(await self.__weekly(itr.author, itr.guild)) 251 | 252 | # DAILY CLAIM 253 | async def __daily(self, user: User, guild: Guild) -> str: 254 | return await self.bot.manager.claim_daily( 255 | user.id, 256 | await self.getGuildId(guild) 257 | ) 258 | 259 | @command(name="daily", description="Claim your daily ajos.") 260 | async def daily_command(self, ctx: Context[Bot]) -> None: 261 | await ctx.reply(await self.__daily(ctx.author, ctx.guild)) 262 | 263 | @slash_command(name="daily", description="Claim your daily ajos.") 264 | async def daily(self, itr: CommandInteraction) -> None: 265 | await itr.send(await self.__daily(itr.author, itr.guild)) 266 | 267 | # DISCOMBOBULATE 268 | async def __discombobulate( 269 | self, 270 | from_user: User, 271 | to_user: User, 272 | amount: int, 273 | guild: Guild 274 | ) -> str: 275 | reply = await self.bot.manager.discombobulate( 276 | from_user.id, 277 | to_user.id, 278 | amount, 279 | await self.getGuildId(guild) 280 | ) 281 | return reply.replace("[[TO_USER]]", f"{to_user}") 282 | 283 | @command(name="discombobulate", description="Discombobulate someone.") 284 | async def discombobulate_command(self, ctx: Context[Bot], user: User, amount: int) -> None: 285 | await ctx.reply(await self.__discombobulate(ctx.author, user, amount, ctx.guild)) 286 | 287 | @slash_command(name="discombobulate", description="Discombobulate someone.") 288 | async def discombobulate( 289 | self, 290 | itr: CommandInteraction, 291 | user: User = Param(description="The user to discombobulate."), 292 | amount: int = Param(description="The amount to offer."), 293 | ) -> None: 294 | await itr.send(await self.__discombobulate(itr.author, user, amount, itr.guild)) 295 | 296 | # ROULETTE 297 | async def __roulette(self) -> str: 298 | return await self.bot.manager.roulette() 299 | 300 | @command(name="roulette", description="Create a roulette") 301 | async def roulette_command(self, ctx: Context[Bot]) -> None: 302 | await ctx.reply(await self.__roulette()) 303 | 304 | @slash_command(name="roulette", description="Create a roulette") 305 | async def roulette(self, itr: CommandInteraction) -> None: 306 | await itr.send(await self.__roulette()) 307 | 308 | # ROULETTE SHOT 309 | async def __roulette_shot(self, user: User, roulette_id: str, guild: Guild) -> str: 310 | return await self.bot.manager.roulette_shot( 311 | user.id, 312 | roulette_id, 313 | await self.getGuildId(guild) 314 | ) 315 | 316 | @command(name="roulette_shot", description="Try your luck") 317 | async def roulette_shot_command(self, ctx: Context[Bot], roulette_id: str) -> None: 318 | await ctx.reply(await self.__roulette_shot(ctx.author, roulette_id, ctx.guild)) 319 | 320 | @slash_command(name="roulette_shot", description="Try your luck") 321 | async def roulette_shot( 322 | self, 323 | itr: CommandInteraction, 324 | roulette_id: str = Param(description="The roulette id") 325 | ) -> None: 326 | await itr.send(await self.__roulette_shot(itr.author, roulette_id, itr.guild)) 327 | 328 | async def __get_inventory(self, author_id = None, inventory = None) -> Embed: 329 | if not inventory: 330 | inventory = await self.bot.manager.get_inventory(author_id) 331 | embed = Embed( 332 | title="Inventory", 333 | colour=0x87CEEB, 334 | ) 335 | for item_name, quantity in inventory.items(): 336 | embed.add_field( 337 | name=item_name, 338 | value=quantity, 339 | inline=True 340 | ) 341 | return embed 342 | 343 | # INVENTORY 344 | @command(name="inventory", description="Get inventory.") 345 | async def inventory_command(self, ctx: Context[Bot]) -> None: 346 | await ctx.reply(embed = await self.__get_inventory(author_id=ctx.author.id)) 347 | 348 | @slash_command(name="inventory", description="Get inventory") 349 | async def inventory( 350 | self, 351 | itr: CommandInteraction, 352 | ) -> None: 353 | await itr.send(embed = await self.__get_inventory(author_id=itr.author.id), ephemeral=True) 354 | 355 | @command(name="verinventory", description="See someone's inventory.") 356 | async def verinventory_command(self, ctx: Context[Bot], user: User) -> None: 357 | res = await self.bot.manager.see_inventory( 358 | ctx.author.id, 359 | user.id, 360 | await self.getGuildId(ctx.guild) 361 | ) 362 | if isinstance(res, str): 363 | await ctx.reply(res) 364 | else: 365 | await ctx.reply(embed = await self.__get_inventory(inventory=res)) 366 | 367 | @slash_command(name="verinventory", description="See someone's inventory.") 368 | async def verinventory( 369 | self, 370 | itr: CommandInteraction, 371 | user: User = Param(description="The user to spy on.") 372 | ) -> None: 373 | res = await self.bot.manager.see_inventory( 374 | itr.author.id, 375 | user.id, 376 | await self.getGuildId(itr.guild) 377 | ) 378 | if isinstance(res, str): 379 | await itr.send(res, ephemeral=True) 380 | else: 381 | await itr.send(embed = await self.__get_inventory(inventory=res), ephemeral=True) 382 | 383 | # INVENTORY USE 384 | async def __use(self, user: User, item: str, guild: Guild) -> dict | str: 385 | return await self.bot.manager.use( 386 | user.id, 387 | item, 388 | await self.getGuildId(guild) 389 | ) 390 | 391 | @command(name="use", description="Use an item from the inventory") 392 | async def use_command(self, ctx: Context[Bot], item: str) -> None: 393 | res = await self.__use(ctx.author, item, ctx.guild) 394 | if isinstance(res, str): 395 | await ctx.reply(res) 396 | else: 397 | await ctx.reply(embed = await self.__build_use_embed(res)) 398 | 399 | @slash_command(name="use", description="Use an item from the inventory") 400 | async def use( 401 | self, 402 | itr: CommandInteraction, 403 | item: str = Param(description="The item to use") 404 | ) -> None: 405 | res = await self.__use(itr.author, item, itr.guild) 406 | if isinstance(res, str): 407 | await itr.send(res, ephemeral=True) 408 | else: 409 | await itr.send(embed = await self.__build_use_embed(res), ephemeral=True) 410 | 411 | async def __set_bomb(self, user: User, time: int, guild: Guild) -> Embed: 412 | message = await self.bot.manager.set_bomb( 413 | user.id, 414 | ":bomb:", 415 | time, 416 | await self.getGuildId(guild) 417 | ) 418 | 419 | return Embed( 420 | timestamp = message.timestamp, 421 | description=message.description, 422 | title=message.title 423 | ) 424 | 425 | 426 | @command(name="set_bomb", description="Set a bomb timer") 427 | async def set_bomb_command(self, ctx: Context[Bot], time: int) -> None: 428 | await ctx.reply(embed=await self.__set_bomb(ctx.author, time, ctx.guild)) 429 | 430 | @slash_command(name="set_bomb", description="Set a bomb timer") 431 | async def set_bomb( 432 | self, 433 | itr: CommandInteraction, 434 | time: int = Param(description="Seconds until the bomb explodes") 435 | ) -> None: 436 | await itr.send(ephemeral=True, embed=await self.__set_bomb(itr.author, time, itr.guild)) 437 | 438 | async def __curse(self, user: User, target: User, guild: Guild) -> str: 439 | return await self.bot.manager.curse( 440 | user.id, 441 | ":magic_wand:", 442 | target.id, 443 | await self.getGuildId(guild) 444 | ) 445 | 446 | @command(name="curse", description="Curse someone") 447 | async def curse_command(self, ctx: Context[Bot], target: User) -> None: 448 | await ctx.reply(await self.__curse(ctx.author, target, ctx.guild)) 449 | 450 | @slash_command(name="curse", description="Curse someone") 451 | async def set_bomb( 452 | self, 453 | itr: CommandInteraction, 454 | target: User = Param(description="Target of the curse") 455 | ) -> None: 456 | await itr.send(await self.__curse(itr.author, target, itr.guild)) 457 | 458 | # INVENTORY TRADE 459 | async def __trade( 460 | self, 461 | from_user: User, 462 | to_user: User, 463 | item: str, 464 | quantity: int, 465 | guild: Guild 466 | ) -> str: 467 | reply = await self.bot.manager.trade( 468 | from_user.id, 469 | to_user.id, 470 | item, 471 | quantity, 472 | await self.getGuildId(guild) 473 | ) 474 | return reply.replace("[[TO_USER]]", f"{to_user}") 475 | 476 | @command(name="trade", description="Trade an item from the inventory") 477 | async def trade_command( 478 | self, 479 | ctx: Context[Bot], 480 | user: User, 481 | item: str, 482 | quantity: int 483 | ) -> None: 484 | await ctx.reply(await self.__trade(ctx.author, user, item, quantity, ctx.guild)) 485 | 486 | @slash_command(name="trade", description="Trade an item from the inventory") 487 | async def trade( 488 | self, 489 | itr: CommandInteraction, 490 | user: User = Param(description="The user to trade to."), 491 | item: str = Param(description="The item to trade."), 492 | quantity: int = Param(description="The quantity to trade."), 493 | ) -> None: 494 | await itr.send(await self.__trade(itr.author, user, item, quantity, itr.guild)) 495 | 496 | # Craft 497 | async def __craft(self, user: User, item: str, guild: Guild) -> str: 498 | return await self.bot.manager.craft(user.id, item, await self.getGuildId(guild)) 499 | 500 | @command(name="craft", description="Craft an item") 501 | async def craft_command(self, ctx: Context[Bot], item: str) -> None: 502 | await ctx.reply(await self.__craft(ctx.author, item, ctx.guild)) 503 | 504 | @slash_command(name="craft", description="Craft an item") 505 | async def craft( 506 | self, 507 | itr: CommandInteraction, 508 | item: str = Param(description="The item to craft") 509 | ) -> None: 510 | await itr.send(await self.__craft(itr.author, item, itr.guild)) 511 | 512 | # EFFECTS 513 | @command(name="effects", description="Show your effects") 514 | async def effects_command(self, ctx: Context[Bot]) -> None: 515 | embed = await self.__build_effects(await self.bot.manager.get_effects(ctx.author.id)) 516 | await ctx.reply(embed=embed) 517 | 518 | @slash_command(name="effects", description="Show your effects") 519 | async def effects(self, itr: CommandInteraction) -> None: 520 | embed = await self.__build_effects(await self.bot.manager.get_effects(itr.author.id)) 521 | await itr.send(embed=embed, ephemeral=True) 522 | 523 | async def __build_effects(self, data) -> Embed: 524 | embed = Embed( 525 | title="Ajo effects", 526 | colour=0x87CEEB, 527 | ) 528 | 529 | for effect, value in data.items(): 530 | embed.add_field( 531 | name=effect, 532 | value=value, 533 | inline=True 534 | ) 535 | 536 | return embed 537 | 538 | async def __build_use_embed(self, data) -> Embed: 539 | embed = Embed( 540 | title="Ajo radar", 541 | colour=0x87CEEB, 542 | ) 543 | 544 | for name, td in data.items(): 545 | embed.add_field( 546 | name=name, 547 | value=td, 548 | inline=True 549 | ) 550 | 551 | return embed 552 | 553 | def setup(bot: Bot) -> None: 554 | bot.add_cog(Ajo(bot)) 555 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. 2 | 3 | [[package]] 4 | name = "aiohappyeyeballs" 5 | version = "2.6.1" 6 | description = "Happy Eyeballs for asyncio" 7 | optional = false 8 | python-versions = ">=3.9" 9 | groups = ["main"] 10 | files = [ 11 | {file = "aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8"}, 12 | {file = "aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558"}, 13 | ] 14 | 15 | [[package]] 16 | name = "aiohttp" 17 | version = "3.11.18" 18 | description = "Async http client/server framework (asyncio)" 19 | optional = false 20 | python-versions = ">=3.9" 21 | groups = ["main"] 22 | files = [ 23 | {file = "aiohttp-3.11.18-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:96264854fedbea933a9ca4b7e0c745728f01380691687b7365d18d9e977179c4"}, 24 | {file = "aiohttp-3.11.18-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9602044ff047043430452bc3a2089743fa85da829e6fc9ee0025351d66c332b6"}, 25 | {file = "aiohttp-3.11.18-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5691dc38750fcb96a33ceef89642f139aa315c8a193bbd42a0c33476fd4a1609"}, 26 | {file = "aiohttp-3.11.18-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:554c918ec43f8480b47a5ca758e10e793bd7410b83701676a4782672d670da55"}, 27 | {file = "aiohttp-3.11.18-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a4076a2b3ba5b004b8cffca6afe18a3b2c5c9ef679b4d1e9859cf76295f8d4f"}, 28 | {file = "aiohttp-3.11.18-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:767a97e6900edd11c762be96d82d13a1d7c4fc4b329f054e88b57cdc21fded94"}, 29 | {file = "aiohttp-3.11.18-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0ddc9337a0fb0e727785ad4f41163cc314376e82b31846d3835673786420ef1"}, 30 | {file = "aiohttp-3.11.18-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f414f37b244f2a97e79b98d48c5ff0789a0b4b4609b17d64fa81771ad780e415"}, 31 | {file = "aiohttp-3.11.18-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fdb239f47328581e2ec7744ab5911f97afb10752332a6dd3d98e14e429e1a9e7"}, 32 | {file = "aiohttp-3.11.18-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:f2c50bad73ed629cc326cc0f75aed8ecfb013f88c5af116f33df556ed47143eb"}, 33 | {file = "aiohttp-3.11.18-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0a8d8f20c39d3fa84d1c28cdb97f3111387e48209e224408e75f29c6f8e0861d"}, 34 | {file = "aiohttp-3.11.18-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:106032eaf9e62fd6bc6578c8b9e6dc4f5ed9a5c1c7fb2231010a1b4304393421"}, 35 | {file = "aiohttp-3.11.18-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:b491e42183e8fcc9901d8dcd8ae644ff785590f1727f76ca86e731c61bfe6643"}, 36 | {file = "aiohttp-3.11.18-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ad8c745ff9460a16b710e58e06a9dec11ebc0d8f4dd82091cefb579844d69868"}, 37 | {file = "aiohttp-3.11.18-cp310-cp310-win32.whl", hash = "sha256:8e57da93e24303a883146510a434f0faf2f1e7e659f3041abc4e3fb3f6702a9f"}, 38 | {file = "aiohttp-3.11.18-cp310-cp310-win_amd64.whl", hash = "sha256:cc93a4121d87d9f12739fc8fab0a95f78444e571ed63e40bfc78cd5abe700ac9"}, 39 | {file = "aiohttp-3.11.18-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:427fdc56ccb6901ff8088544bde47084845ea81591deb16f957897f0f0ba1be9"}, 40 | {file = "aiohttp-3.11.18-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c828b6d23b984255b85b9b04a5b963a74278b7356a7de84fda5e3b76866597b"}, 41 | {file = "aiohttp-3.11.18-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5c2eaa145bb36b33af1ff2860820ba0589e165be4ab63a49aebfd0981c173b66"}, 42 | {file = "aiohttp-3.11.18-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d518ce32179f7e2096bf4e3e8438cf445f05fedd597f252de9f54c728574756"}, 43 | {file = "aiohttp-3.11.18-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0700055a6e05c2f4711011a44364020d7a10fbbcd02fbf3e30e8f7e7fddc8717"}, 44 | {file = "aiohttp-3.11.18-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8bd1cde83e4684324e6ee19adfc25fd649d04078179890be7b29f76b501de8e4"}, 45 | {file = "aiohttp-3.11.18-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73b8870fe1c9a201b8c0d12c94fe781b918664766728783241a79e0468427e4f"}, 46 | {file = "aiohttp-3.11.18-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:25557982dd36b9e32c0a3357f30804e80790ec2c4d20ac6bcc598533e04c6361"}, 47 | {file = "aiohttp-3.11.18-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7e889c9df381a2433802991288a61e5a19ceb4f61bd14f5c9fa165655dcb1fd1"}, 48 | {file = "aiohttp-3.11.18-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:9ea345fda05bae217b6cce2acf3682ce3b13d0d16dd47d0de7080e5e21362421"}, 49 | {file = "aiohttp-3.11.18-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9f26545b9940c4b46f0a9388fd04ee3ad7064c4017b5a334dd450f616396590e"}, 50 | {file = "aiohttp-3.11.18-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:3a621d85e85dccabd700294494d7179ed1590b6d07a35709bb9bd608c7f5dd1d"}, 51 | {file = "aiohttp-3.11.18-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9c23fd8d08eb9c2af3faeedc8c56e134acdaf36e2117ee059d7defa655130e5f"}, 52 | {file = "aiohttp-3.11.18-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9e6b0e519067caa4fd7fb72e3e8002d16a68e84e62e7291092a5433763dc0dd"}, 53 | {file = "aiohttp-3.11.18-cp311-cp311-win32.whl", hash = "sha256:122f3e739f6607e5e4c6a2f8562a6f476192a682a52bda8b4c6d4254e1138f4d"}, 54 | {file = "aiohttp-3.11.18-cp311-cp311-win_amd64.whl", hash = "sha256:e6f3c0a3a1e73e88af384b2e8a0b9f4fb73245afd47589df2afcab6b638fa0e6"}, 55 | {file = "aiohttp-3.11.18-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:63d71eceb9cad35d47d71f78edac41fcd01ff10cacaa64e473d1aec13fa02df2"}, 56 | {file = "aiohttp-3.11.18-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d1929da615840969929e8878d7951b31afe0bac883d84418f92e5755d7b49508"}, 57 | {file = "aiohttp-3.11.18-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d0aebeb2392f19b184e3fdd9e651b0e39cd0f195cdb93328bd124a1d455cd0e"}, 58 | {file = "aiohttp-3.11.18-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3849ead845e8444f7331c284132ab314b4dac43bfae1e3cf350906d4fff4620f"}, 59 | {file = "aiohttp-3.11.18-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5e8452ad6b2863709f8b3d615955aa0807bc093c34b8e25b3b52097fe421cb7f"}, 60 | {file = "aiohttp-3.11.18-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b8d2b42073611c860a37f718b3d61ae8b4c2b124b2e776e2c10619d920350ec"}, 61 | {file = "aiohttp-3.11.18-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40fbf91f6a0ac317c0a07eb328a1384941872f6761f2e6f7208b63c4cc0a7ff6"}, 62 | {file = "aiohttp-3.11.18-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ff5625413fec55216da5eaa011cf6b0a2ed67a565914a212a51aa3755b0009"}, 63 | {file = "aiohttp-3.11.18-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7f33a92a2fde08e8c6b0c61815521324fc1612f397abf96eed86b8e31618fdb4"}, 64 | {file = "aiohttp-3.11.18-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:11d5391946605f445ddafda5eab11caf310f90cdda1fd99865564e3164f5cff9"}, 65 | {file = "aiohttp-3.11.18-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3cc314245deb311364884e44242e00c18b5896e4fe6d5f942e7ad7e4cb640adb"}, 66 | {file = "aiohttp-3.11.18-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0f421843b0f70740772228b9e8093289924359d306530bcd3926f39acbe1adda"}, 67 | {file = "aiohttp-3.11.18-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e220e7562467dc8d589e31c1acd13438d82c03d7f385c9cd41a3f6d1d15807c1"}, 68 | {file = "aiohttp-3.11.18-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ab2ef72f8605046115bc9aa8e9d14fd49086d405855f40b79ed9e5c1f9f4faea"}, 69 | {file = "aiohttp-3.11.18-cp312-cp312-win32.whl", hash = "sha256:12a62691eb5aac58d65200c7ae94d73e8a65c331c3a86a2e9670927e94339ee8"}, 70 | {file = "aiohttp-3.11.18-cp312-cp312-win_amd64.whl", hash = "sha256:364329f319c499128fd5cd2d1c31c44f234c58f9b96cc57f743d16ec4f3238c8"}, 71 | {file = "aiohttp-3.11.18-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:474215ec618974054cf5dc465497ae9708543cbfc312c65212325d4212525811"}, 72 | {file = "aiohttp-3.11.18-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6ced70adf03920d4e67c373fd692123e34d3ac81dfa1c27e45904a628567d804"}, 73 | {file = "aiohttp-3.11.18-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2d9f6c0152f8d71361905aaf9ed979259537981f47ad099c8b3d81e0319814bd"}, 74 | {file = "aiohttp-3.11.18-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a35197013ed929c0aed5c9096de1fc5a9d336914d73ab3f9df14741668c0616c"}, 75 | {file = "aiohttp-3.11.18-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:540b8a1f3a424f1af63e0af2d2853a759242a1769f9f1ab053996a392bd70118"}, 76 | {file = "aiohttp-3.11.18-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9e6710ebebfce2ba21cee6d91e7452d1125100f41b906fb5af3da8c78b764c1"}, 77 | {file = "aiohttp-3.11.18-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8af2ef3b4b652ff109f98087242e2ab974b2b2b496304063585e3d78de0b000"}, 78 | {file = "aiohttp-3.11.18-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:28c3f975e5ae3dbcbe95b7e3dcd30e51da561a0a0f2cfbcdea30fc1308d72137"}, 79 | {file = "aiohttp-3.11.18-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c28875e316c7b4c3e745172d882d8a5c835b11018e33432d281211af35794a93"}, 80 | {file = "aiohttp-3.11.18-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:13cd38515568ae230e1ef6919e2e33da5d0f46862943fcda74e7e915096815f3"}, 81 | {file = "aiohttp-3.11.18-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0e2a92101efb9f4c2942252c69c63ddb26d20f46f540c239ccfa5af865197bb8"}, 82 | {file = "aiohttp-3.11.18-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:e6d3e32b8753c8d45ac550b11a1090dd66d110d4ef805ffe60fa61495360b3b2"}, 83 | {file = "aiohttp-3.11.18-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ea4cf2488156e0f281f93cc2fd365025efcba3e2d217cbe3df2840f8c73db261"}, 84 | {file = "aiohttp-3.11.18-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d4df95ad522c53f2b9ebc07f12ccd2cb15550941e11a5bbc5ddca2ca56316d7"}, 85 | {file = "aiohttp-3.11.18-cp313-cp313-win32.whl", hash = "sha256:cdd1bbaf1e61f0d94aced116d6e95fe25942f7a5f42382195fd9501089db5d78"}, 86 | {file = "aiohttp-3.11.18-cp313-cp313-win_amd64.whl", hash = "sha256:bdd619c27e44382cf642223f11cfd4d795161362a5a1fc1fa3940397bc89db01"}, 87 | {file = "aiohttp-3.11.18-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:469ac32375d9a716da49817cd26f1916ec787fc82b151c1c832f58420e6d3533"}, 88 | {file = "aiohttp-3.11.18-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3cec21dd68924179258ae14af9f5418c1ebdbba60b98c667815891293902e5e0"}, 89 | {file = "aiohttp-3.11.18-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b426495fb9140e75719b3ae70a5e8dd3a79def0ae3c6c27e012fc59f16544a4a"}, 90 | {file = "aiohttp-3.11.18-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad2f41203e2808616292db5d7170cccf0c9f9c982d02544443c7eb0296e8b0c7"}, 91 | {file = "aiohttp-3.11.18-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5bc0ae0a5e9939e423e065a3e5b00b24b8379f1db46046d7ab71753dfc7dd0e1"}, 92 | {file = "aiohttp-3.11.18-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe7cdd3f7d1df43200e1c80f1aed86bb36033bf65e3c7cf46a2b97a253ef8798"}, 93 | {file = "aiohttp-3.11.18-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5199be2a2f01ffdfa8c3a6f5981205242986b9e63eb8ae03fd18f736e4840721"}, 94 | {file = "aiohttp-3.11.18-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ccec9e72660b10f8e283e91aa0295975c7bd85c204011d9f5eb69310555cf30"}, 95 | {file = "aiohttp-3.11.18-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1596ebf17e42e293cbacc7a24c3e0dc0f8f755b40aff0402cb74c1ff6baec1d3"}, 96 | {file = "aiohttp-3.11.18-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:eab7b040a8a873020113ba814b7db7fa935235e4cbaf8f3da17671baa1024863"}, 97 | {file = "aiohttp-3.11.18-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:5d61df4a05476ff891cff0030329fee4088d40e4dc9b013fac01bc3c745542c2"}, 98 | {file = "aiohttp-3.11.18-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:46533e6792e1410f9801d09fd40cbbff3f3518d1b501d6c3c5b218f427f6ff08"}, 99 | {file = "aiohttp-3.11.18-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:c1b90407ced992331dd6d4f1355819ea1c274cc1ee4d5b7046c6761f9ec11829"}, 100 | {file = "aiohttp-3.11.18-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a2fd04ae4971b914e54fe459dd7edbbd3f2ba875d69e057d5e3c8e8cac094935"}, 101 | {file = "aiohttp-3.11.18-cp39-cp39-win32.whl", hash = "sha256:b2f317d1678002eee6fe85670039fb34a757972284614638f82b903a03feacdc"}, 102 | {file = "aiohttp-3.11.18-cp39-cp39-win_amd64.whl", hash = "sha256:5e7007b8d1d09bce37b54111f593d173691c530b80f27c6493b928dabed9e6ef"}, 103 | {file = "aiohttp-3.11.18.tar.gz", hash = "sha256:ae856e1138612b7e412db63b7708735cff4d38d0399f6a5435d3dac2669f558a"}, 104 | ] 105 | 106 | [package.dependencies] 107 | aiohappyeyeballs = ">=2.3.0" 108 | aiosignal = ">=1.1.2" 109 | async-timeout = {version = ">=4.0,<6.0", markers = "python_version < \"3.11\""} 110 | attrs = ">=17.3.0" 111 | frozenlist = ">=1.1.1" 112 | multidict = ">=4.5,<7.0" 113 | propcache = ">=0.2.0" 114 | yarl = ">=1.17.0,<2.0" 115 | 116 | [package.extras] 117 | speedups = ["Brotli ; platform_python_implementation == \"CPython\"", "aiodns (>=3.2.0) ; sys_platform == \"linux\" or sys_platform == \"darwin\"", "brotlicffi ; platform_python_implementation != \"CPython\""] 118 | 119 | [[package]] 120 | name = "aiosignal" 121 | version = "1.3.2" 122 | description = "aiosignal: a list of registered asynchronous callbacks" 123 | optional = false 124 | python-versions = ">=3.9" 125 | groups = ["main"] 126 | files = [ 127 | {file = "aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5"}, 128 | {file = "aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54"}, 129 | ] 130 | 131 | [package.dependencies] 132 | frozenlist = ">=1.1.0" 133 | 134 | [[package]] 135 | name = "ajobot-manager-v5" 136 | version = "1.3.1" 137 | description = "An interface for the different ajobots" 138 | optional = false 139 | python-versions = "<4.0,>=3.10" 140 | groups = ["main"] 141 | files = [ 142 | {file = "ajobot_manager_v5-1.3.1-py3-none-any.whl", hash = "sha256:028c5061bb30e0647cbeaf43df9e791bb9213afdf0dd1c80757f32a52ab6f8f5"}, 143 | {file = "ajobot_manager_v5-1.3.1.tar.gz", hash = "sha256:b8bf8be015742354f8456dd01d45ba9c55c62d30eafd0b2a4d853349d2cdf5dc"}, 144 | ] 145 | 146 | [package.dependencies] 147 | loguru = ">=0.6.0,<0.7.0" 148 | redis = ">=7.1.0,<8.0.0" 149 | 150 | [[package]] 151 | name = "async-timeout" 152 | version = "5.0.1" 153 | description = "Timeout context manager for asyncio programs" 154 | optional = false 155 | python-versions = ">=3.8" 156 | groups = ["main"] 157 | markers = "python_full_version < \"3.11.3\"" 158 | files = [ 159 | {file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"}, 160 | {file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"}, 161 | ] 162 | 163 | [[package]] 164 | name = "attrs" 165 | version = "25.3.0" 166 | description = "Classes Without Boilerplate" 167 | optional = false 168 | python-versions = ">=3.8" 169 | groups = ["main"] 170 | files = [ 171 | {file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"}, 172 | {file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"}, 173 | ] 174 | 175 | [package.extras] 176 | benchmark = ["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-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] 177 | cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "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]"] 178 | dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] 179 | docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier"] 180 | 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]"] 181 | 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\""] 182 | 183 | [[package]] 184 | name = "black" 185 | version = "21.12b0" 186 | description = "The uncompromising code formatter." 187 | optional = false 188 | python-versions = ">=3.6.2" 189 | groups = ["dev"] 190 | files = [ 191 | {file = "black-21.12b0-py3-none-any.whl", hash = "sha256:a615e69ae185e08fdd73e4715e260e2479c861b5740057fde6e8b4e3b7dd589f"}, 192 | {file = "black-21.12b0.tar.gz", hash = "sha256:77b80f693a569e2e527958459634f18df9b0ba2625ba4e0c2d5da5be42e6f2b3"}, 193 | ] 194 | 195 | [package.dependencies] 196 | click = ">=7.1.2" 197 | mypy-extensions = ">=0.4.3" 198 | pathspec = ">=0.9.0,<1" 199 | platformdirs = ">=2" 200 | tomli = ">=0.2.6,<2.0.0" 201 | typing-extensions = {version = ">=3.10.0.0,<3.10.0.1 || >3.10.0.1", markers = "python_version >= \"3.10\""} 202 | 203 | [package.extras] 204 | colorama = ["colorama (>=0.4.3)"] 205 | d = ["aiohttp (>=3.7.4)"] 206 | jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] 207 | python2 = ["typed-ast (>=1.4.3)"] 208 | uvloop = ["uvloop (>=0.15.2)"] 209 | 210 | [[package]] 211 | name = "certifi" 212 | version = "2025.4.26" 213 | description = "Python package for providing Mozilla's CA Bundle." 214 | optional = false 215 | python-versions = ">=3.6" 216 | groups = ["main"] 217 | files = [ 218 | {file = "certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3"}, 219 | {file = "certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6"}, 220 | ] 221 | 222 | [[package]] 223 | name = "cfgv" 224 | version = "3.4.0" 225 | description = "Validate configuration and produce human readable error messages." 226 | optional = false 227 | python-versions = ">=3.8" 228 | groups = ["dev"] 229 | files = [ 230 | {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, 231 | {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, 232 | ] 233 | 234 | [[package]] 235 | name = "click" 236 | version = "8.1.8" 237 | description = "Composable command line interface toolkit" 238 | optional = false 239 | python-versions = ">=3.7" 240 | groups = ["dev"] 241 | files = [ 242 | {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, 243 | {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, 244 | ] 245 | 246 | [package.dependencies] 247 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 248 | 249 | [[package]] 250 | name = "colorama" 251 | version = "0.4.6" 252 | description = "Cross-platform colored terminal text." 253 | optional = false 254 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 255 | groups = ["main", "dev"] 256 | files = [ 257 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 258 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 259 | ] 260 | markers = {dev = "platform_system == \"Windows\""} 261 | 262 | [[package]] 263 | name = "disnake" 264 | version = "2.10.1" 265 | description = "A Python wrapper for the Discord API" 266 | optional = false 267 | python-versions = ">=3.8" 268 | groups = ["main"] 269 | files = [ 270 | {file = "disnake-2.10.1-py3-none-any.whl", hash = "sha256:60175459cf5ada0381fc174d693f862c3f0b78e58ce51c39fd9960d86072409d"}, 271 | {file = "disnake-2.10.1.tar.gz", hash = "sha256:e469938c72f88432b55bc5f521a260f027d3db9e5337fe0f28098f3bfafc5a43"}, 272 | ] 273 | 274 | [package.dependencies] 275 | aiohttp = ">=3.7.0,<4.0" 276 | 277 | [package.extras] 278 | docs = ["sphinx (==7.0.1)", "sphinx-autobuild (>=2021.3,<2022.0)", "sphinx-hoverxref (==1.3.0)", "sphinx-notfound-page (==0.8.3)", "sphinxcontrib-towncrier (==0.3.2a0)", "sphinxcontrib-trio (>=1.1.2,<1.2.0)", "towncrier (==23.6.0)"] 279 | speed = ["Brotli", "aiodns (>=1.1)", "cchardet ; python_version < \"3.10\"", "orjson (>=3.6,<4.0)"] 280 | voice = ["PyNaCl (>=1.5.0,<1.6)", "audioop-lts (==0.2.1) ; python_version >= \"3.13\""] 281 | 282 | [[package]] 283 | name = "distlib" 284 | version = "0.3.9" 285 | description = "Distribution utilities" 286 | optional = false 287 | python-versions = "*" 288 | groups = ["dev"] 289 | files = [ 290 | {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, 291 | {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, 292 | ] 293 | 294 | [[package]] 295 | name = "filelock" 296 | version = "3.18.0" 297 | description = "A platform independent file lock." 298 | optional = false 299 | python-versions = ">=3.9" 300 | groups = ["dev"] 301 | files = [ 302 | {file = "filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de"}, 303 | {file = "filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2"}, 304 | ] 305 | 306 | [package.extras] 307 | docs = ["furo (>=2024.8.6)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] 308 | testing = ["covdefaults (>=2.3)", "coverage (>=7.6.10)", "diff-cover (>=9.2.1)", "pytest (>=8.3.4)", "pytest-asyncio (>=0.25.2)", "pytest-cov (>=6)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.28.1)"] 309 | typing = ["typing-extensions (>=4.12.2) ; python_version < \"3.11\""] 310 | 311 | [[package]] 312 | name = "frozenlist" 313 | version = "1.6.0" 314 | description = "A list-like structure which implements collections.abc.MutableSequence" 315 | optional = false 316 | python-versions = ">=3.9" 317 | groups = ["main"] 318 | files = [ 319 | {file = "frozenlist-1.6.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e6e558ea1e47fd6fa8ac9ccdad403e5dd5ecc6ed8dda94343056fa4277d5c65e"}, 320 | {file = "frozenlist-1.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f4b3cd7334a4bbc0c472164f3744562cb72d05002cc6fcf58adb104630bbc352"}, 321 | {file = "frozenlist-1.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9799257237d0479736e2b4c01ff26b5c7f7694ac9692a426cb717f3dc02fff9b"}, 322 | {file = "frozenlist-1.6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3a7bb0fe1f7a70fb5c6f497dc32619db7d2cdd53164af30ade2f34673f8b1fc"}, 323 | {file = "frozenlist-1.6.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:36d2fc099229f1e4237f563b2a3e0ff7ccebc3999f729067ce4e64a97a7f2869"}, 324 | {file = "frozenlist-1.6.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f27a9f9a86dcf00708be82359db8de86b80d029814e6693259befe82bb58a106"}, 325 | {file = "frozenlist-1.6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75ecee69073312951244f11b8627e3700ec2bfe07ed24e3a685a5979f0412d24"}, 326 | {file = "frozenlist-1.6.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2c7d5aa19714b1b01a0f515d078a629e445e667b9da869a3cd0e6fe7dec78bd"}, 327 | {file = "frozenlist-1.6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69bbd454f0fb23b51cadc9bdba616c9678e4114b6f9fa372d462ff2ed9323ec8"}, 328 | {file = "frozenlist-1.6.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7daa508e75613809c7a57136dec4871a21bca3080b3a8fc347c50b187df4f00c"}, 329 | {file = "frozenlist-1.6.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:89ffdb799154fd4d7b85c56d5fa9d9ad48946619e0eb95755723fffa11022d75"}, 330 | {file = "frozenlist-1.6.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:920b6bd77d209931e4c263223381d63f76828bec574440f29eb497cf3394c249"}, 331 | {file = "frozenlist-1.6.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d3ceb265249fb401702fce3792e6b44c1166b9319737d21495d3611028d95769"}, 332 | {file = "frozenlist-1.6.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:52021b528f1571f98a7d4258c58aa8d4b1a96d4f01d00d51f1089f2e0323cb02"}, 333 | {file = "frozenlist-1.6.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0f2ca7810b809ed0f1917293050163c7654cefc57a49f337d5cd9de717b8fad3"}, 334 | {file = "frozenlist-1.6.0-cp310-cp310-win32.whl", hash = "sha256:0e6f8653acb82e15e5443dba415fb62a8732b68fe09936bb6d388c725b57f812"}, 335 | {file = "frozenlist-1.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:f1a39819a5a3e84304cd286e3dc62a549fe60985415851b3337b6f5cc91907f1"}, 336 | {file = "frozenlist-1.6.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ae8337990e7a45683548ffb2fee1af2f1ed08169284cd829cdd9a7fa7470530d"}, 337 | {file = "frozenlist-1.6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8c952f69dd524558694818a461855f35d36cc7f5c0adddce37e962c85d06eac0"}, 338 | {file = "frozenlist-1.6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8f5fef13136c4e2dee91bfb9a44e236fff78fc2cd9f838eddfc470c3d7d90afe"}, 339 | {file = "frozenlist-1.6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:716bbba09611b4663ecbb7cd022f640759af8259e12a6ca939c0a6acd49eedba"}, 340 | {file = "frozenlist-1.6.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7b8c4dc422c1a3ffc550b465090e53b0bf4839047f3e436a34172ac67c45d595"}, 341 | {file = "frozenlist-1.6.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b11534872256e1666116f6587a1592ef395a98b54476addb5e8d352925cb5d4a"}, 342 | {file = "frozenlist-1.6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c6eceb88aaf7221f75be6ab498dc622a151f5f88d536661af3ffc486245a626"}, 343 | {file = "frozenlist-1.6.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62c828a5b195570eb4b37369fcbbd58e96c905768d53a44d13044355647838ff"}, 344 | {file = "frozenlist-1.6.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1c6bd2c6399920c9622362ce95a7d74e7f9af9bfec05fff91b8ce4b9647845a"}, 345 | {file = "frozenlist-1.6.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:49ba23817781e22fcbd45fd9ff2b9b8cdb7b16a42a4851ab8025cae7b22e96d0"}, 346 | {file = "frozenlist-1.6.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:431ef6937ae0f853143e2ca67d6da76c083e8b1fe3df0e96f3802fd37626e606"}, 347 | {file = "frozenlist-1.6.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9d124b38b3c299ca68433597ee26b7819209cb8a3a9ea761dfe9db3a04bba584"}, 348 | {file = "frozenlist-1.6.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:118e97556306402e2b010da1ef21ea70cb6d6122e580da64c056b96f524fbd6a"}, 349 | {file = "frozenlist-1.6.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fb3b309f1d4086b5533cf7bbcf3f956f0ae6469664522f1bde4feed26fba60f1"}, 350 | {file = "frozenlist-1.6.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54dece0d21dce4fdb188a1ffc555926adf1d1c516e493c2914d7c370e454bc9e"}, 351 | {file = "frozenlist-1.6.0-cp311-cp311-win32.whl", hash = "sha256:654e4ba1d0b2154ca2f096bed27461cf6160bc7f504a7f9a9ef447c293caf860"}, 352 | {file = "frozenlist-1.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:3e911391bffdb806001002c1f860787542f45916c3baf764264a52765d5a5603"}, 353 | {file = "frozenlist-1.6.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c5b9e42ace7d95bf41e19b87cec8f262c41d3510d8ad7514ab3862ea2197bfb1"}, 354 | {file = "frozenlist-1.6.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ca9973735ce9f770d24d5484dcb42f68f135351c2fc81a7a9369e48cf2998a29"}, 355 | {file = "frozenlist-1.6.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6ac40ec76041c67b928ca8aaffba15c2b2ee3f5ae8d0cb0617b5e63ec119ca25"}, 356 | {file = "frozenlist-1.6.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95b7a8a3180dfb280eb044fdec562f9b461614c0ef21669aea6f1d3dac6ee576"}, 357 | {file = "frozenlist-1.6.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c444d824e22da6c9291886d80c7d00c444981a72686e2b59d38b285617cb52c8"}, 358 | {file = "frozenlist-1.6.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb52c8166499a8150bfd38478248572c924c003cbb45fe3bcd348e5ac7c000f9"}, 359 | {file = "frozenlist-1.6.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b35298b2db9c2468106278537ee529719228950a5fdda686582f68f247d1dc6e"}, 360 | {file = "frozenlist-1.6.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d108e2d070034f9d57210f22fefd22ea0d04609fc97c5f7f5a686b3471028590"}, 361 | {file = "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"}, 362 | {file = "frozenlist-1.6.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:94bb451c664415f02f07eef4ece976a2c65dcbab9c2f1705b7031a3a75349d8c"}, 363 | {file = "frozenlist-1.6.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:d1a686d0b0949182b8faddea596f3fc11f44768d1f74d4cad70213b2e139d821"}, 364 | {file = "frozenlist-1.6.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:ea8e59105d802c5a38bdbe7362822c522230b3faba2aa35c0fa1765239b7dd70"}, 365 | {file = "frozenlist-1.6.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:abc4e880a9b920bc5020bf6a431a6bb40589d9bca3975c980495f63632e8382f"}, 366 | {file = "frozenlist-1.6.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9a79713adfe28830f27a3c62f6b5406c37376c892b05ae070906f07ae4487046"}, 367 | {file = "frozenlist-1.6.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9a0318c2068e217a8f5e3b85e35899f5a19e97141a45bb925bb357cfe1daf770"}, 368 | {file = "frozenlist-1.6.0-cp312-cp312-win32.whl", hash = "sha256:853ac025092a24bb3bf09ae87f9127de9fe6e0c345614ac92536577cf956dfcc"}, 369 | {file = "frozenlist-1.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:2bdfe2d7e6c9281c6e55523acd6c2bf77963cb422fdc7d142fb0cb6621b66878"}, 370 | {file = "frozenlist-1.6.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1d7fb014fe0fbfee3efd6a94fc635aeaa68e5e1720fe9e57357f2e2c6e1a647e"}, 371 | {file = "frozenlist-1.6.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01bcaa305a0fdad12745502bfd16a1c75b14558dabae226852f9159364573117"}, 372 | {file = "frozenlist-1.6.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8b314faa3051a6d45da196a2c495e922f987dc848e967d8cfeaee8a0328b1cd4"}, 373 | {file = "frozenlist-1.6.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da62fecac21a3ee10463d153549d8db87549a5e77eefb8c91ac84bb42bb1e4e3"}, 374 | {file = "frozenlist-1.6.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d1eb89bf3454e2132e046f9599fbcf0a4483ed43b40f545551a39316d0201cd1"}, 375 | {file = "frozenlist-1.6.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18689b40cb3936acd971f663ccb8e2589c45db5e2c5f07e0ec6207664029a9c"}, 376 | {file = "frozenlist-1.6.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e67ddb0749ed066b1a03fba812e2dcae791dd50e5da03be50b6a14d0c1a9ee45"}, 377 | {file = "frozenlist-1.6.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fc5e64626e6682638d6e44398c9baf1d6ce6bc236d40b4b57255c9d3f9761f1f"}, 378 | {file = "frozenlist-1.6.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:437cfd39564744ae32ad5929e55b18ebd88817f9180e4cc05e7d53b75f79ce85"}, 379 | {file = "frozenlist-1.6.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:62dd7df78e74d924952e2feb7357d826af8d2f307557a779d14ddf94d7311be8"}, 380 | {file = "frozenlist-1.6.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a66781d7e4cddcbbcfd64de3d41a61d6bdde370fc2e38623f30b2bd539e84a9f"}, 381 | {file = "frozenlist-1.6.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:482fe06e9a3fffbcd41950f9d890034b4a54395c60b5e61fae875d37a699813f"}, 382 | {file = "frozenlist-1.6.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:e4f9373c500dfc02feea39f7a56e4f543e670212102cc2eeb51d3a99c7ffbde6"}, 383 | {file = "frozenlist-1.6.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e69bb81de06827147b7bfbaeb284d85219fa92d9f097e32cc73675f279d70188"}, 384 | {file = "frozenlist-1.6.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7613d9977d2ab4a9141dde4a149f4357e4065949674c5649f920fec86ecb393e"}, 385 | {file = "frozenlist-1.6.0-cp313-cp313-win32.whl", hash = "sha256:4def87ef6d90429f777c9d9de3961679abf938cb6b7b63d4a7eb8a268babfce4"}, 386 | {file = "frozenlist-1.6.0-cp313-cp313-win_amd64.whl", hash = "sha256:37a8a52c3dfff01515e9bbbee0e6063181362f9de3db2ccf9bc96189b557cbfd"}, 387 | {file = "frozenlist-1.6.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:46138f5a0773d064ff663d273b309b696293d7a7c00a0994c5c13a5078134b64"}, 388 | {file = "frozenlist-1.6.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:f88bc0a2b9c2a835cb888b32246c27cdab5740059fb3688852bf91e915399b91"}, 389 | {file = "frozenlist-1.6.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:777704c1d7655b802c7850255639672e90e81ad6fa42b99ce5ed3fbf45e338dd"}, 390 | {file = "frozenlist-1.6.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85ef8d41764c7de0dcdaf64f733a27352248493a85a80661f3c678acd27e31f2"}, 391 | {file = "frozenlist-1.6.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:da5cb36623f2b846fb25009d9d9215322318ff1c63403075f812b3b2876c8506"}, 392 | {file = "frozenlist-1.6.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cbb56587a16cf0fb8acd19e90ff9924979ac1431baea8681712716a8337577b0"}, 393 | {file = "frozenlist-1.6.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6154c3ba59cda3f954c6333025369e42c3acd0c6e8b6ce31eb5c5b8116c07e0"}, 394 | {file = "frozenlist-1.6.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e8246877afa3f1ae5c979fe85f567d220f86a50dc6c493b9b7d8191181ae01e"}, 395 | {file = "frozenlist-1.6.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b0f6cce16306d2e117cf9db71ab3a9e8878a28176aeaf0dbe35248d97b28d0c"}, 396 | {file = "frozenlist-1.6.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:1b8e8cd8032ba266f91136d7105706ad57770f3522eac4a111d77ac126a25a9b"}, 397 | {file = "frozenlist-1.6.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:e2ada1d8515d3ea5378c018a5f6d14b4994d4036591a52ceaf1a1549dec8e1ad"}, 398 | {file = "frozenlist-1.6.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:cdb2c7f071e4026c19a3e32b93a09e59b12000751fc9b0b7758da899e657d215"}, 399 | {file = "frozenlist-1.6.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:03572933a1969a6d6ab509d509e5af82ef80d4a5d4e1e9f2e1cdd22c77a3f4d2"}, 400 | {file = "frozenlist-1.6.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:77effc978947548b676c54bbd6a08992759ea6f410d4987d69feea9cd0919911"}, 401 | {file = "frozenlist-1.6.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a2bda8be77660ad4089caf2223fdbd6db1858462c4b85b67fbfa22102021e497"}, 402 | {file = "frozenlist-1.6.0-cp313-cp313t-win32.whl", hash = "sha256:a4d96dc5bcdbd834ec6b0f91027817214216b5b30316494d2b1aebffb87c534f"}, 403 | {file = "frozenlist-1.6.0-cp313-cp313t-win_amd64.whl", hash = "sha256:e18036cb4caa17ea151fd5f3d70be9d354c99eb8cf817a3ccde8a7873b074348"}, 404 | {file = "frozenlist-1.6.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:536a1236065c29980c15c7229fbb830dedf809708c10e159b8136534233545f0"}, 405 | {file = "frozenlist-1.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ed5e3a4462ff25ca84fb09e0fada8ea267df98a450340ead4c91b44857267d70"}, 406 | {file = "frozenlist-1.6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e19c0fc9f4f030fcae43b4cdec9e8ab83ffe30ec10c79a4a43a04d1af6c5e1ad"}, 407 | {file = "frozenlist-1.6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7c608f833897501dac548585312d73a7dca028bf3b8688f0d712b7acfaf7fb3"}, 408 | {file = "frozenlist-1.6.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0dbae96c225d584f834b8d3cc688825911960f003a85cb0fd20b6e5512468c42"}, 409 | {file = "frozenlist-1.6.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:625170a91dd7261a1d1c2a0c1a353c9e55d21cd67d0852185a5fef86587e6f5f"}, 410 | {file = "frozenlist-1.6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1db8b2fc7ee8a940b547a14c10e56560ad3ea6499dc6875c354e2335812f739d"}, 411 | {file = "frozenlist-1.6.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4da6fc43048b648275a220e3a61c33b7fff65d11bdd6dcb9d9c145ff708b804c"}, 412 | {file = "frozenlist-1.6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ef8e7e8f2f3820c5f175d70fdd199b79e417acf6c72c5d0aa8f63c9f721646f"}, 413 | {file = "frozenlist-1.6.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:aa733d123cc78245e9bb15f29b44ed9e5780dc6867cfc4e544717b91f980af3b"}, 414 | {file = "frozenlist-1.6.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:ba7f8d97152b61f22d7f59491a781ba9b177dd9f318486c5fbc52cde2db12189"}, 415 | {file = "frozenlist-1.6.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:56a0b8dd6d0d3d971c91f1df75e824986667ccce91e20dca2023683814344791"}, 416 | {file = "frozenlist-1.6.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:5c9e89bf19ca148efcc9e3c44fd4c09d5af85c8a7dd3dbd0da1cb83425ef4983"}, 417 | {file = "frozenlist-1.6.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:1330f0a4376587face7637dfd245380a57fe21ae8f9d360c1c2ef8746c4195fa"}, 418 | {file = "frozenlist-1.6.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2187248203b59625566cac53572ec8c2647a140ee2738b4e36772930377a533c"}, 419 | {file = "frozenlist-1.6.0-cp39-cp39-win32.whl", hash = "sha256:2b8cf4cfea847d6c12af06091561a89740f1f67f331c3fa8623391905e878530"}, 420 | {file = "frozenlist-1.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:1255d5d64328c5a0d066ecb0f02034d086537925f1f04b50b1ae60d37afbf572"}, 421 | {file = "frozenlist-1.6.0-py3-none-any.whl", hash = "sha256:535eec9987adb04701266b92745d6cdcef2e77669299359c3009c3404dd5d191"}, 422 | {file = "frozenlist-1.6.0.tar.gz", hash = "sha256:b99655c32c1c8e06d111e7f41c06c29a5318cb1835df23a45518e02a47c63b68"}, 423 | ] 424 | 425 | [[package]] 426 | name = "identify" 427 | version = "2.6.10" 428 | description = "File identification library for Python" 429 | optional = false 430 | python-versions = ">=3.9" 431 | groups = ["dev"] 432 | files = [ 433 | {file = "identify-2.6.10-py2.py3-none-any.whl", hash = "sha256:5f34248f54136beed1a7ba6a6b5c4b6cf21ff495aac7c359e1ef831ae3b8ab25"}, 434 | {file = "identify-2.6.10.tar.gz", hash = "sha256:45e92fd704f3da71cc3880036633f48b4b7265fd4de2b57627cb157216eb7eb8"}, 435 | ] 436 | 437 | [package.extras] 438 | license = ["ukkonen"] 439 | 440 | [[package]] 441 | name = "idna" 442 | version = "3.10" 443 | description = "Internationalized Domain Names in Applications (IDNA)" 444 | optional = false 445 | python-versions = ">=3.6" 446 | groups = ["main"] 447 | files = [ 448 | {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, 449 | {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, 450 | ] 451 | 452 | [package.extras] 453 | all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] 454 | 455 | [[package]] 456 | name = "isort" 457 | version = "5.13.2" 458 | description = "A Python utility / library to sort Python imports." 459 | optional = false 460 | python-versions = ">=3.8.0" 461 | groups = ["dev"] 462 | files = [ 463 | {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, 464 | {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, 465 | ] 466 | 467 | [package.extras] 468 | colors = ["colorama (>=0.4.6)"] 469 | 470 | [[package]] 471 | name = "loguru" 472 | version = "0.6.0" 473 | description = "Python logging made (stupidly) simple" 474 | optional = false 475 | python-versions = ">=3.5" 476 | groups = ["main"] 477 | files = [ 478 | {file = "loguru-0.6.0-py3-none-any.whl", hash = "sha256:4e2414d534a2ab57573365b3e6d0234dfb1d84b68b7f3b948e6fb743860a77c3"}, 479 | {file = "loguru-0.6.0.tar.gz", hash = "sha256:066bd06758d0a513e9836fd9c6b5a75bfb3fd36841f4b996bc60b547a309d41c"}, 480 | ] 481 | 482 | [package.dependencies] 483 | colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""} 484 | win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} 485 | 486 | [package.extras] 487 | dev = ["Sphinx (>=4.1.1) ; python_version >= \"3.6\"", "black (>=19.10b0) ; python_version >= \"3.6\"", "colorama (>=0.3.4)", "docutils (==0.16)", "flake8 (>=3.7.7)", "isort (>=5.1.1) ; python_version >= \"3.6\"", "pytest (>=4.6.2)", "pytest-cov (>=2.7.1)", "sphinx-autobuild (>=0.7.1) ; python_version >= \"3.6\"", "sphinx-rtd-theme (>=0.4.3) ; python_version >= \"3.6\"", "tox (>=3.9.0)"] 488 | 489 | [[package]] 490 | name = "mslex" 491 | version = "0.3.0" 492 | description = "shlex for windows" 493 | optional = false 494 | python-versions = ">=3.5" 495 | groups = ["main"] 496 | markers = "sys_platform == \"win32\"" 497 | files = [ 498 | {file = "mslex-0.3.0-py2.py3-none-any.whl", hash = "sha256:380cb14abf8fabf40e56df5c8b21a6d533dc5cbdcfe42406bbf08dda8f42e42a"}, 499 | {file = "mslex-0.3.0.tar.gz", hash = "sha256:4a1ac3f25025cad78ad2fe499dd16d42759f7a3801645399cce5c404415daa97"}, 500 | ] 501 | 502 | [[package]] 503 | name = "multidict" 504 | version = "6.4.3" 505 | description = "multidict implementation" 506 | optional = false 507 | python-versions = ">=3.9" 508 | groups = ["main"] 509 | files = [ 510 | {file = "multidict-6.4.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:32a998bd8a64ca48616eac5a8c1cc4fa38fb244a3facf2eeb14abe186e0f6cc5"}, 511 | {file = "multidict-6.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a54ec568f1fc7f3c313c2f3b16e5db346bf3660e1309746e7fccbbfded856188"}, 512 | {file = "multidict-6.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a7be07e5df178430621c716a63151165684d3e9958f2bbfcb644246162007ab7"}, 513 | {file = "multidict-6.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b128dbf1c939674a50dd0b28f12c244d90e5015e751a4f339a96c54f7275e291"}, 514 | {file = "multidict-6.4.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b9cb19dfd83d35b6ff24a4022376ea6e45a2beba8ef3f0836b8a4b288b6ad685"}, 515 | {file = "multidict-6.4.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3cf62f8e447ea2c1395afa289b332e49e13d07435369b6f4e41f887db65b40bf"}, 516 | {file = "multidict-6.4.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:909f7d43ff8f13d1adccb6a397094adc369d4da794407f8dd592c51cf0eae4b1"}, 517 | {file = "multidict-6.4.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0bb8f8302fbc7122033df959e25777b0b7659b1fd6bcb9cb6bed76b5de67afef"}, 518 | {file = "multidict-6.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:224b79471b4f21169ea25ebc37ed6f058040c578e50ade532e2066562597b8a9"}, 519 | {file = "multidict-6.4.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a7bd27f7ab3204f16967a6f899b3e8e9eb3362c0ab91f2ee659e0345445e0078"}, 520 | {file = "multidict-6.4.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:99592bd3162e9c664671fd14e578a33bfdba487ea64bcb41d281286d3c870ad7"}, 521 | {file = "multidict-6.4.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:a62d78a1c9072949018cdb05d3c533924ef8ac9bcb06cbf96f6d14772c5cd451"}, 522 | {file = "multidict-6.4.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:3ccdde001578347e877ca4f629450973c510e88e8865d5aefbcb89b852ccc666"}, 523 | {file = "multidict-6.4.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:eccb67b0e78aa2e38a04c5ecc13bab325a43e5159a181a9d1a6723db913cbb3c"}, 524 | {file = "multidict-6.4.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8b6fcf6054fc4114a27aa865f8840ef3d675f9316e81868e0ad5866184a6cba5"}, 525 | {file = "multidict-6.4.3-cp310-cp310-win32.whl", hash = "sha256:f92c7f62d59373cd93bc9969d2da9b4b21f78283b1379ba012f7ee8127b3152e"}, 526 | {file = "multidict-6.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:b57e28dbc031d13916b946719f213c494a517b442d7b48b29443e79610acd887"}, 527 | {file = "multidict-6.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f6f19170197cc29baccd33ccc5b5d6a331058796485857cf34f7635aa25fb0cd"}, 528 | {file = "multidict-6.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f2882bf27037eb687e49591690e5d491e677272964f9ec7bc2abbe09108bdfb8"}, 529 | {file = "multidict-6.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fbf226ac85f7d6b6b9ba77db4ec0704fde88463dc17717aec78ec3c8546c70ad"}, 530 | {file = "multidict-6.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e329114f82ad4b9dd291bef614ea8971ec119ecd0f54795109976de75c9a852"}, 531 | {file = "multidict-6.4.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1f4e0334d7a555c63f5c8952c57ab6f1c7b4f8c7f3442df689fc9f03df315c08"}, 532 | {file = "multidict-6.4.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:740915eb776617b57142ce0bb13b7596933496e2f798d3d15a20614adf30d229"}, 533 | {file = "multidict-6.4.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255dac25134d2b141c944b59a0d2f7211ca12a6d4779f7586a98b4b03ea80508"}, 534 | {file = "multidict-6.4.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4e8535bd4d741039b5aad4285ecd9b902ef9e224711f0b6afda6e38d7ac02c7"}, 535 | {file = "multidict-6.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c433a33be000dd968f5750722eaa0991037be0be4a9d453eba121774985bc8"}, 536 | {file = "multidict-6.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4eb33b0bdc50acd538f45041f5f19945a1f32b909b76d7b117c0c25d8063df56"}, 537 | {file = "multidict-6.4.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:75482f43465edefd8a5d72724887ccdcd0c83778ded8f0cb1e0594bf71736cc0"}, 538 | {file = "multidict-6.4.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ce5b3082e86aee80b3925ab4928198450d8e5b6466e11501fe03ad2191c6d777"}, 539 | {file = "multidict-6.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e413152e3212c4d39f82cf83c6f91be44bec9ddea950ce17af87fbf4e32ca6b2"}, 540 | {file = "multidict-6.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:8aac2eeff69b71f229a405c0a4b61b54bade8e10163bc7b44fcd257949620618"}, 541 | {file = "multidict-6.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ab583ac203af1d09034be41458feeab7863c0635c650a16f15771e1386abf2d7"}, 542 | {file = "multidict-6.4.3-cp311-cp311-win32.whl", hash = "sha256:1b2019317726f41e81154df636a897de1bfe9228c3724a433894e44cd2512378"}, 543 | {file = "multidict-6.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:43173924fa93c7486402217fab99b60baf78d33806af299c56133a3755f69589"}, 544 | {file = "multidict-6.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1f1c2f58f08b36f8475f3ec6f5aeb95270921d418bf18f90dffd6be5c7b0e676"}, 545 | {file = "multidict-6.4.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:26ae9ad364fc61b936fb7bf4c9d8bd53f3a5b4417142cd0be5c509d6f767e2f1"}, 546 | {file = "multidict-6.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:659318c6c8a85f6ecfc06b4e57529e5a78dfdd697260cc81f683492ad7e9435a"}, 547 | {file = "multidict-6.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1eb72c741fd24d5a28242ce72bb61bc91f8451877131fa3fe930edb195f7054"}, 548 | {file = "multidict-6.4.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3cd06d88cb7398252284ee75c8db8e680aa0d321451132d0dba12bc995f0adcc"}, 549 | {file = "multidict-6.4.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4543d8dc6470a82fde92b035a92529317191ce993533c3c0c68f56811164ed07"}, 550 | {file = "multidict-6.4.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:30a3ebdc068c27e9d6081fca0e2c33fdf132ecea703a72ea216b81a66860adde"}, 551 | {file = "multidict-6.4.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b038f10e23f277153f86f95c777ba1958bcd5993194fda26a1d06fae98b2f00c"}, 552 | {file = "multidict-6.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c605a2b2dc14282b580454b9b5d14ebe0668381a3a26d0ac39daa0ca115eb2ae"}, 553 | {file = "multidict-6.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8bd2b875f4ca2bb527fe23e318ddd509b7df163407b0fb717df229041c6df5d3"}, 554 | {file = "multidict-6.4.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c2e98c840c9c8e65c0e04b40c6c5066c8632678cd50c8721fdbcd2e09f21a507"}, 555 | {file = "multidict-6.4.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:66eb80dd0ab36dbd559635e62fba3083a48a252633164857a1d1684f14326427"}, 556 | {file = "multidict-6.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c23831bdee0a2a3cf21be057b5e5326292f60472fb6c6f86392bbf0de70ba731"}, 557 | {file = "multidict-6.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1535cec6443bfd80d028052e9d17ba6ff8a5a3534c51d285ba56c18af97e9713"}, 558 | {file = "multidict-6.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3b73e7227681f85d19dec46e5b881827cd354aabe46049e1a61d2f9aaa4e285a"}, 559 | {file = "multidict-6.4.3-cp312-cp312-win32.whl", hash = "sha256:8eac0c49df91b88bf91f818e0a24c1c46f3622978e2c27035bfdca98e0e18124"}, 560 | {file = "multidict-6.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:11990b5c757d956cd1db7cb140be50a63216af32cd6506329c2c59d732d802db"}, 561 | {file = "multidict-6.4.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7a76534263d03ae0cfa721fea40fd2b5b9d17a6f85e98025931d41dc49504474"}, 562 | {file = "multidict-6.4.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:805031c2f599eee62ac579843555ed1ce389ae00c7e9f74c2a1b45e0564a88dd"}, 563 | {file = "multidict-6.4.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c56c179839d5dcf51d565132185409d1d5dd8e614ba501eb79023a6cab25576b"}, 564 | {file = "multidict-6.4.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c64f4ddb3886dd8ab71b68a7431ad4aa01a8fa5be5b11543b29674f29ca0ba3"}, 565 | {file = "multidict-6.4.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3002a856367c0b41cad6784f5b8d3ab008eda194ed7864aaa58f65312e2abcac"}, 566 | {file = "multidict-6.4.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3d75e621e7d887d539d6e1d789f0c64271c250276c333480a9e1de089611f790"}, 567 | {file = "multidict-6.4.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:995015cf4a3c0d72cbf453b10a999b92c5629eaf3a0c3e1efb4b5c1f602253bb"}, 568 | {file = "multidict-6.4.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2b0fabae7939d09d7d16a711468c385272fa1b9b7fb0d37e51143585d8e72e0"}, 569 | {file = "multidict-6.4.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:61ed4d82f8a1e67eb9eb04f8587970d78fe7cddb4e4d6230b77eda23d27938f9"}, 570 | {file = "multidict-6.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:062428944a8dc69df9fdc5d5fc6279421e5f9c75a9ee3f586f274ba7b05ab3c8"}, 571 | {file = "multidict-6.4.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:b90e27b4674e6c405ad6c64e515a505c6d113b832df52fdacb6b1ffd1fa9a1d1"}, 572 | {file = "multidict-6.4.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7d50d4abf6729921e9613d98344b74241572b751c6b37feed75fb0c37bd5a817"}, 573 | {file = "multidict-6.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:43fe10524fb0a0514be3954be53258e61d87341008ce4914f8e8b92bee6f875d"}, 574 | {file = "multidict-6.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:236966ca6c472ea4e2d3f02f6673ebfd36ba3f23159c323f5a496869bc8e47c9"}, 575 | {file = "multidict-6.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:422a5ec315018e606473ba1f5431e064cf8b2a7468019233dcf8082fabad64c8"}, 576 | {file = "multidict-6.4.3-cp313-cp313-win32.whl", hash = "sha256:f901a5aace8e8c25d78960dcc24c870c8d356660d3b49b93a78bf38eb682aac3"}, 577 | {file = "multidict-6.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:1c152c49e42277bc9a2f7b78bd5fa10b13e88d1b0328221e7aef89d5c60a99a5"}, 578 | {file = "multidict-6.4.3-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:be8751869e28b9c0d368d94f5afcb4234db66fe8496144547b4b6d6a0645cfc6"}, 579 | {file = "multidict-6.4.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0d4b31f8a68dccbcd2c0ea04f0e014f1defc6b78f0eb8b35f2265e8716a6df0c"}, 580 | {file = "multidict-6.4.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:032efeab3049e37eef2ff91271884303becc9e54d740b492a93b7e7266e23756"}, 581 | {file = "multidict-6.4.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e78006af1a7c8a8007e4f56629d7252668344442f66982368ac06522445e375"}, 582 | {file = "multidict-6.4.3-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:daeac9dd30cda8703c417e4fddccd7c4dc0c73421a0b54a7da2713be125846be"}, 583 | {file = "multidict-6.4.3-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f6f90700881438953eae443a9c6f8a509808bc3b185246992c4233ccee37fea"}, 584 | {file = "multidict-6.4.3-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f84627997008390dd15762128dcf73c3365f4ec0106739cde6c20a07ed198ec8"}, 585 | {file = "multidict-6.4.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3307b48cd156153b117c0ea54890a3bdbf858a5b296ddd40dc3852e5f16e9b02"}, 586 | {file = "multidict-6.4.3-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ead46b0fa1dcf5af503a46e9f1c2e80b5d95c6011526352fa5f42ea201526124"}, 587 | {file = "multidict-6.4.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:1748cb2743bedc339d63eb1bca314061568793acd603a6e37b09a326334c9f44"}, 588 | {file = "multidict-6.4.3-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:acc9fa606f76fc111b4569348cc23a771cb52c61516dcc6bcef46d612edb483b"}, 589 | {file = "multidict-6.4.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:31469d5832b5885adeb70982e531ce86f8c992334edd2f2254a10fa3182ac504"}, 590 | {file = "multidict-6.4.3-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:ba46b51b6e51b4ef7bfb84b82f5db0dc5e300fb222a8a13b8cd4111898a869cf"}, 591 | {file = "multidict-6.4.3-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:389cfefb599edf3fcfd5f64c0410da686f90f5f5e2c4d84e14f6797a5a337af4"}, 592 | {file = "multidict-6.4.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:64bc2bbc5fba7b9db5c2c8d750824f41c6994e3882e6d73c903c2afa78d091e4"}, 593 | {file = "multidict-6.4.3-cp313-cp313t-win32.whl", hash = "sha256:0ecdc12ea44bab2807d6b4a7e5eef25109ab1c82a8240d86d3c1fc9f3b72efd5"}, 594 | {file = "multidict-6.4.3-cp313-cp313t-win_amd64.whl", hash = "sha256:7146a8742ea71b5d7d955bffcef58a9e6e04efba704b52a460134fefd10a8208"}, 595 | {file = "multidict-6.4.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5427a2679e95a642b7f8b0f761e660c845c8e6fe3141cddd6b62005bd133fc21"}, 596 | {file = "multidict-6.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:24a8caa26521b9ad09732972927d7b45b66453e6ebd91a3c6a46d811eeb7349b"}, 597 | {file = "multidict-6.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6b5a272bc7c36a2cd1b56ddc6bff02e9ce499f9f14ee4a45c45434ef083f2459"}, 598 | {file = "multidict-6.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edf74dc5e212b8c75165b435c43eb0d5e81b6b300a938a4eb82827119115e840"}, 599 | {file = "multidict-6.4.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9f35de41aec4b323c71f54b0ca461ebf694fb48bec62f65221f52e0017955b39"}, 600 | {file = "multidict-6.4.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae93e0ff43b6f6892999af64097b18561691ffd835e21a8348a441e256592e1f"}, 601 | {file = "multidict-6.4.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e3929269e9d7eff905d6971d8b8c85e7dbc72c18fb99c8eae6fe0a152f2e343"}, 602 | {file = "multidict-6.4.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb6214fe1750adc2a1b801a199d64b5a67671bf76ebf24c730b157846d0e90d2"}, 603 | {file = "multidict-6.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6d79cf5c0c6284e90f72123f4a3e4add52d6c6ebb4a9054e88df15b8d08444c6"}, 604 | {file = "multidict-6.4.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2427370f4a255262928cd14533a70d9738dfacadb7563bc3b7f704cc2360fc4e"}, 605 | {file = "multidict-6.4.3-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:fbd8d737867912b6c5f99f56782b8cb81f978a97b4437a1c476de90a3e41c9a1"}, 606 | {file = "multidict-6.4.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:0ee1bf613c448997f73fc4efb4ecebebb1c02268028dd4f11f011f02300cf1e8"}, 607 | {file = "multidict-6.4.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:578568c4ba5f2b8abd956baf8b23790dbfdc953e87d5b110bce343b4a54fc9e7"}, 608 | {file = "multidict-6.4.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:a059ad6b80de5b84b9fa02a39400319e62edd39d210b4e4f8c4f1243bdac4752"}, 609 | {file = "multidict-6.4.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:dd53893675b729a965088aaadd6a1f326a72b83742b056c1065bdd2e2a42b4df"}, 610 | {file = "multidict-6.4.3-cp39-cp39-win32.whl", hash = "sha256:abcfed2c4c139f25c2355e180bcc077a7cae91eefbb8b3927bb3f836c9586f1f"}, 611 | {file = "multidict-6.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:b1b389ae17296dd739015d5ddb222ee99fd66adeae910de21ac950e00979d897"}, 612 | {file = "multidict-6.4.3-py3-none-any.whl", hash = "sha256:59fe01ee8e2a1e8ceb3f6dbb216b09c8d9f4ef1c22c4fc825d045a147fa2ebc9"}, 613 | {file = "multidict-6.4.3.tar.gz", hash = "sha256:3ada0b058c9f213c5f95ba301f922d402ac234f1111a7d8fd70f1b99f3c281ec"}, 614 | ] 615 | 616 | [package.dependencies] 617 | typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.11\""} 618 | 619 | [[package]] 620 | name = "mypy-extensions" 621 | version = "1.1.0" 622 | description = "Type system extensions for programs checked with the mypy type checker." 623 | optional = false 624 | python-versions = ">=3.8" 625 | groups = ["dev"] 626 | files = [ 627 | {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, 628 | {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, 629 | ] 630 | 631 | [[package]] 632 | name = "nodeenv" 633 | version = "1.9.1" 634 | description = "Node.js virtual environment builder" 635 | optional = false 636 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 637 | groups = ["dev"] 638 | files = [ 639 | {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, 640 | {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, 641 | ] 642 | 643 | [[package]] 644 | name = "pathspec" 645 | version = "0.12.1" 646 | description = "Utility library for gitignore style pattern matching of file paths." 647 | optional = false 648 | python-versions = ">=3.8" 649 | groups = ["dev"] 650 | files = [ 651 | {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, 652 | {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, 653 | ] 654 | 655 | [[package]] 656 | name = "platformdirs" 657 | version = "4.3.7" 658 | description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." 659 | optional = false 660 | python-versions = ">=3.9" 661 | groups = ["dev"] 662 | files = [ 663 | {file = "platformdirs-4.3.7-py3-none-any.whl", hash = "sha256:a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94"}, 664 | {file = "platformdirs-4.3.7.tar.gz", hash = "sha256:eb437d586b6a0986388f0d6f74aa0cde27b48d0e3d66843640bfb6bdcdb6e351"}, 665 | ] 666 | 667 | [package.extras] 668 | docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] 669 | test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)"] 670 | type = ["mypy (>=1.14.1)"] 671 | 672 | [[package]] 673 | name = "pre-commit" 674 | version = "2.21.0" 675 | description = "A framework for managing and maintaining multi-language pre-commit hooks." 676 | optional = false 677 | python-versions = ">=3.7" 678 | groups = ["dev"] 679 | files = [ 680 | {file = "pre_commit-2.21.0-py2.py3-none-any.whl", hash = "sha256:e2f91727039fc39a92f58a588a25b87f936de6567eed4f0e673e0507edc75bad"}, 681 | {file = "pre_commit-2.21.0.tar.gz", hash = "sha256:31ef31af7e474a8d8995027fefdfcf509b5c913ff31f2015b4ec4beb26a6f658"}, 682 | ] 683 | 684 | [package.dependencies] 685 | cfgv = ">=2.0.0" 686 | identify = ">=1.0.0" 687 | nodeenv = ">=0.11.1" 688 | pyyaml = ">=5.1" 689 | virtualenv = ">=20.10.0" 690 | 691 | [[package]] 692 | name = "propcache" 693 | version = "0.3.1" 694 | description = "Accelerated property cache" 695 | optional = false 696 | python-versions = ">=3.9" 697 | groups = ["main"] 698 | files = [ 699 | {file = "propcache-0.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f27785888d2fdd918bc36de8b8739f2d6c791399552333721b58193f68ea3e98"}, 700 | {file = "propcache-0.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4e89cde74154c7b5957f87a355bb9c8ec929c167b59c83d90654ea36aeb6180"}, 701 | {file = "propcache-0.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:730178f476ef03d3d4d255f0c9fa186cb1d13fd33ffe89d39f2cda4da90ceb71"}, 702 | {file = "propcache-0.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:967a8eec513dbe08330f10137eacb427b2ca52118769e82ebcfcab0fba92a649"}, 703 | {file = "propcache-0.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b9145c35cc87313b5fd480144f8078716007656093d23059e8993d3a8fa730f"}, 704 | {file = "propcache-0.3.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e64e948ab41411958670f1093c0a57acfdc3bee5cf5b935671bbd5313bcf229"}, 705 | {file = "propcache-0.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:319fa8765bfd6a265e5fa661547556da381e53274bc05094fc9ea50da51bfd46"}, 706 | {file = "propcache-0.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c66d8ccbc902ad548312b96ed8d5d266d0d2c6d006fd0f66323e9d8f2dd49be7"}, 707 | {file = "propcache-0.3.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2d219b0dbabe75e15e581fc1ae796109b07c8ba7d25b9ae8d650da582bed01b0"}, 708 | {file = "propcache-0.3.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:cd6a55f65241c551eb53f8cf4d2f4af33512c39da5d9777694e9d9c60872f519"}, 709 | {file = "propcache-0.3.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9979643ffc69b799d50d3a7b72b5164a2e97e117009d7af6dfdd2ab906cb72cd"}, 710 | {file = "propcache-0.3.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4cf9e93a81979f1424f1a3d155213dc928f1069d697e4353edb8a5eba67c6259"}, 711 | {file = "propcache-0.3.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2fce1df66915909ff6c824bbb5eb403d2d15f98f1518e583074671a30fe0c21e"}, 712 | {file = "propcache-0.3.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4d0dfdd9a2ebc77b869a0b04423591ea8823f791293b527dc1bb896c1d6f1136"}, 713 | {file = "propcache-0.3.1-cp310-cp310-win32.whl", hash = "sha256:1f6cc0ad7b4560e5637eb2c994e97b4fa41ba8226069c9277eb5ea7101845b42"}, 714 | {file = "propcache-0.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:47ef24aa6511e388e9894ec16f0fbf3313a53ee68402bc428744a367ec55b833"}, 715 | {file = "propcache-0.3.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7f30241577d2fef2602113b70ef7231bf4c69a97e04693bde08ddab913ba0ce5"}, 716 | {file = "propcache-0.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:43593c6772aa12abc3af7784bff4a41ffa921608dd38b77cf1dfd7f5c4e71371"}, 717 | {file = "propcache-0.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a75801768bbe65499495660b777e018cbe90c7980f07f8aa57d6be79ea6f71da"}, 718 | {file = "propcache-0.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6f1324db48f001c2ca26a25fa25af60711e09b9aaf4b28488602776f4f9a744"}, 719 | {file = "propcache-0.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cdb0f3e1eb6dfc9965d19734d8f9c481b294b5274337a8cb5cb01b462dcb7e0"}, 720 | {file = "propcache-0.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1eb34d90aac9bfbced9a58b266f8946cb5935869ff01b164573a7634d39fbcb5"}, 721 | {file = "propcache-0.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f35c7070eeec2cdaac6fd3fe245226ed2a6292d3ee8c938e5bb645b434c5f256"}, 722 | {file = "propcache-0.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b23c11c2c9e6d4e7300c92e022046ad09b91fd00e36e83c44483df4afa990073"}, 723 | {file = "propcache-0.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3e19ea4ea0bf46179f8a3652ac1426e6dcbaf577ce4b4f65be581e237340420d"}, 724 | {file = "propcache-0.3.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:bd39c92e4c8f6cbf5f08257d6360123af72af9f4da75a690bef50da77362d25f"}, 725 | {file = "propcache-0.3.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b0313e8b923b3814d1c4a524c93dfecea5f39fa95601f6a9b1ac96cd66f89ea0"}, 726 | {file = "propcache-0.3.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e861ad82892408487be144906a368ddbe2dc6297074ade2d892341b35c59844a"}, 727 | {file = "propcache-0.3.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:61014615c1274df8da5991a1e5da85a3ccb00c2d4701ac6f3383afd3ca47ab0a"}, 728 | {file = "propcache-0.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:71ebe3fe42656a2328ab08933d420df5f3ab121772eef78f2dc63624157f0ed9"}, 729 | {file = "propcache-0.3.1-cp311-cp311-win32.whl", hash = "sha256:58aa11f4ca8b60113d4b8e32d37e7e78bd8af4d1a5b5cb4979ed856a45e62005"}, 730 | {file = "propcache-0.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:9532ea0b26a401264b1365146c440a6d78269ed41f83f23818d4b79497aeabe7"}, 731 | {file = "propcache-0.3.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f78eb8422acc93d7b69964012ad7048764bb45a54ba7a39bb9e146c72ea29723"}, 732 | {file = "propcache-0.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:89498dd49c2f9a026ee057965cdf8192e5ae070ce7d7a7bd4b66a8e257d0c976"}, 733 | {file = "propcache-0.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:09400e98545c998d57d10035ff623266927cb784d13dd2b31fd33b8a5316b85b"}, 734 | {file = "propcache-0.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa8efd8c5adc5a2c9d3b952815ff8f7710cefdcaf5f2c36d26aff51aeca2f12f"}, 735 | {file = "propcache-0.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c2fe5c910f6007e716a06d269608d307b4f36e7babee5f36533722660e8c4a70"}, 736 | {file = "propcache-0.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a0ab8cf8cdd2194f8ff979a43ab43049b1df0b37aa64ab7eca04ac14429baeb7"}, 737 | {file = "propcache-0.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:563f9d8c03ad645597b8d010ef4e9eab359faeb11a0a2ac9f7b4bc8c28ebef25"}, 738 | {file = "propcache-0.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb6e0faf8cb6b4beea5d6ed7b5a578254c6d7df54c36ccd3d8b3eb00d6770277"}, 739 | {file = "propcache-0.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1c5c7ab7f2bb3f573d1cb921993006ba2d39e8621019dffb1c5bc94cdbae81e8"}, 740 | {file = "propcache-0.3.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:050b571b2e96ec942898f8eb46ea4bfbb19bd5502424747e83badc2d4a99a44e"}, 741 | {file = "propcache-0.3.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e1c4d24b804b3a87e9350f79e2371a705a188d292fd310e663483af6ee6718ee"}, 742 | {file = "propcache-0.3.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e4fe2a6d5ce975c117a6bb1e8ccda772d1e7029c1cca1acd209f91d30fa72815"}, 743 | {file = "propcache-0.3.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:feccd282de1f6322f56f6845bf1207a537227812f0a9bf5571df52bb418d79d5"}, 744 | {file = "propcache-0.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ec314cde7314d2dd0510c6787326bbffcbdc317ecee6b7401ce218b3099075a7"}, 745 | {file = "propcache-0.3.1-cp312-cp312-win32.whl", hash = "sha256:7d2d5a0028d920738372630870e7d9644ce437142197f8c827194fca404bf03b"}, 746 | {file = "propcache-0.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:88c423efef9d7a59dae0614eaed718449c09a5ac79a5f224a8b9664d603f04a3"}, 747 | {file = "propcache-0.3.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f1528ec4374617a7a753f90f20e2f551121bb558fcb35926f99e3c42367164b8"}, 748 | {file = "propcache-0.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dc1915ec523b3b494933b5424980831b636fe483d7d543f7afb7b3bf00f0c10f"}, 749 | {file = "propcache-0.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a110205022d077da24e60b3df8bcee73971be9575dec5573dd17ae5d81751111"}, 750 | {file = "propcache-0.3.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d249609e547c04d190e820d0d4c8ca03ed4582bcf8e4e160a6969ddfb57b62e5"}, 751 | {file = "propcache-0.3.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ced33d827625d0a589e831126ccb4f5c29dfdf6766cac441d23995a65825dcb"}, 752 | {file = "propcache-0.3.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4114c4ada8f3181af20808bedb250da6bae56660e4b8dfd9cd95d4549c0962f7"}, 753 | {file = "propcache-0.3.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:975af16f406ce48f1333ec5e912fe11064605d5c5b3f6746969077cc3adeb120"}, 754 | {file = "propcache-0.3.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a34aa3a1abc50740be6ac0ab9d594e274f59960d3ad253cd318af76b996dd654"}, 755 | {file = "propcache-0.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9cec3239c85ed15bfaded997773fdad9fb5662b0a7cbc854a43f291eb183179e"}, 756 | {file = "propcache-0.3.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:05543250deac8e61084234d5fc54f8ebd254e8f2b39a16b1dce48904f45b744b"}, 757 | {file = "propcache-0.3.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5cb5918253912e088edbf023788de539219718d3b10aef334476b62d2b53de53"}, 758 | {file = "propcache-0.3.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f3bbecd2f34d0e6d3c543fdb3b15d6b60dd69970c2b4c822379e5ec8f6f621d5"}, 759 | {file = "propcache-0.3.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aca63103895c7d960a5b9b044a83f544b233c95e0dcff114389d64d762017af7"}, 760 | {file = "propcache-0.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5a0a9898fdb99bf11786265468571e628ba60af80dc3f6eb89a3545540c6b0ef"}, 761 | {file = "propcache-0.3.1-cp313-cp313-win32.whl", hash = "sha256:3a02a28095b5e63128bcae98eb59025924f121f048a62393db682f049bf4ac24"}, 762 | {file = "propcache-0.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:813fbb8b6aea2fc9659815e585e548fe706d6f663fa73dff59a1677d4595a037"}, 763 | {file = "propcache-0.3.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a444192f20f5ce8a5e52761a031b90f5ea6288b1eef42ad4c7e64fef33540b8f"}, 764 | {file = "propcache-0.3.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0fbe94666e62ebe36cd652f5fc012abfbc2342de99b523f8267a678e4dfdee3c"}, 765 | {file = "propcache-0.3.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f011f104db880f4e2166bcdcf7f58250f7a465bc6b068dc84c824a3d4a5c94dc"}, 766 | {file = "propcache-0.3.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e584b6d388aeb0001d6d5c2bd86b26304adde6d9bb9bfa9c4889805021b96de"}, 767 | {file = "propcache-0.3.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a17583515a04358b034e241f952f1715243482fc2c2945fd99a1b03a0bd77d6"}, 768 | {file = "propcache-0.3.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5aed8d8308215089c0734a2af4f2e95eeb360660184ad3912686c181e500b2e7"}, 769 | {file = "propcache-0.3.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d8e309ff9a0503ef70dc9a0ebd3e69cf7b3894c9ae2ae81fc10943c37762458"}, 770 | {file = "propcache-0.3.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b655032b202028a582d27aeedc2e813299f82cb232f969f87a4fde491a233f11"}, 771 | {file = "propcache-0.3.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f64d91b751df77931336b5ff7bafbe8845c5770b06630e27acd5dbb71e1931c"}, 772 | {file = "propcache-0.3.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:19a06db789a4bd896ee91ebc50d059e23b3639c25d58eb35be3ca1cbe967c3bf"}, 773 | {file = "propcache-0.3.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:bef100c88d8692864651b5f98e871fb090bd65c8a41a1cb0ff2322db39c96c27"}, 774 | {file = "propcache-0.3.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:87380fb1f3089d2a0b8b00f006ed12bd41bd858fabfa7330c954c70f50ed8757"}, 775 | {file = "propcache-0.3.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e474fc718e73ba5ec5180358aa07f6aded0ff5f2abe700e3115c37d75c947e18"}, 776 | {file = "propcache-0.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:17d1c688a443355234f3c031349da69444be052613483f3e4158eef751abcd8a"}, 777 | {file = "propcache-0.3.1-cp313-cp313t-win32.whl", hash = "sha256:359e81a949a7619802eb601d66d37072b79b79c2505e6d3fd8b945538411400d"}, 778 | {file = "propcache-0.3.1-cp313-cp313t-win_amd64.whl", hash = "sha256:e7fb9a84c9abbf2b2683fa3e7b0d7da4d8ecf139a1c635732a8bda29c5214b0e"}, 779 | {file = "propcache-0.3.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ed5f6d2edbf349bd8d630e81f474d33d6ae5d07760c44d33cd808e2f5c8f4ae6"}, 780 | {file = "propcache-0.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:668ddddc9f3075af019f784456267eb504cb77c2c4bd46cc8402d723b4d200bf"}, 781 | {file = "propcache-0.3.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0c86e7ceea56376216eba345aa1fc6a8a6b27ac236181f840d1d7e6a1ea9ba5c"}, 782 | {file = "propcache-0.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83be47aa4e35b87c106fc0c84c0fc069d3f9b9b06d3c494cd404ec6747544894"}, 783 | {file = "propcache-0.3.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:27c6ac6aa9fc7bc662f594ef380707494cb42c22786a558d95fcdedb9aa5d035"}, 784 | {file = "propcache-0.3.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64a956dff37080b352c1c40b2966b09defb014347043e740d420ca1eb7c9b908"}, 785 | {file = "propcache-0.3.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82de5da8c8893056603ac2d6a89eb8b4df49abf1a7c19d536984c8dd63f481d5"}, 786 | {file = "propcache-0.3.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c3c3a203c375b08fd06a20da3cf7aac293b834b6f4f4db71190e8422750cca5"}, 787 | {file = "propcache-0.3.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:b303b194c2e6f171cfddf8b8ba30baefccf03d36a4d9cab7fd0bb68ba476a3d7"}, 788 | {file = "propcache-0.3.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:916cd229b0150129d645ec51614d38129ee74c03293a9f3f17537be0029a9641"}, 789 | {file = "propcache-0.3.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:a461959ead5b38e2581998700b26346b78cd98540b5524796c175722f18b0294"}, 790 | {file = "propcache-0.3.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:069e7212890b0bcf9b2be0a03afb0c2d5161d91e1bf51569a64f629acc7defbf"}, 791 | {file = "propcache-0.3.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ef2e4e91fb3945769e14ce82ed53007195e616a63aa43b40fb7ebaaf907c8d4c"}, 792 | {file = "propcache-0.3.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8638f99dca15b9dff328fb6273e09f03d1c50d9b6512f3b65a4154588a7595fe"}, 793 | {file = "propcache-0.3.1-cp39-cp39-win32.whl", hash = "sha256:6f173bbfe976105aaa890b712d1759de339d8a7cef2fc0a1714cc1a1e1c47f64"}, 794 | {file = "propcache-0.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:603f1fe4144420374f1a69b907494c3acbc867a581c2d49d4175b0de7cc64566"}, 795 | {file = "propcache-0.3.1-py3-none-any.whl", hash = "sha256:9a8ecf38de50a7f518c21568c80f985e776397b902f1ce0b01f799aba1608b40"}, 796 | {file = "propcache-0.3.1.tar.gz", hash = "sha256:40d980c33765359098837527e18eddefc9a24cea5b45e078a7f3bb5b032c6ecf"}, 797 | ] 798 | 799 | [[package]] 800 | name = "psutil" 801 | version = "5.9.8" 802 | description = "Cross-platform lib for process and system monitoring in Python." 803 | optional = false 804 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" 805 | groups = ["main"] 806 | files = [ 807 | {file = "psutil-5.9.8-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:26bd09967ae00920df88e0352a91cff1a78f8d69b3ecabbfe733610c0af486c8"}, 808 | {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:05806de88103b25903dff19bb6692bd2e714ccf9e668d050d144012055cbca73"}, 809 | {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:611052c4bc70432ec770d5d54f64206aa7203a101ec273a0cd82418c86503bb7"}, 810 | {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:50187900d73c1381ba1454cf40308c2bf6f34268518b3f36a9b663ca87e65e36"}, 811 | {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:02615ed8c5ea222323408ceba16c60e99c3f91639b07da6373fb7e6539abc56d"}, 812 | {file = "psutil-5.9.8-cp27-none-win32.whl", hash = "sha256:36f435891adb138ed3c9e58c6af3e2e6ca9ac2f365efe1f9cfef2794e6c93b4e"}, 813 | {file = "psutil-5.9.8-cp27-none-win_amd64.whl", hash = "sha256:bd1184ceb3f87651a67b2708d4c3338e9b10c5df903f2e3776b62303b26cb631"}, 814 | {file = "psutil-5.9.8-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:aee678c8720623dc456fa20659af736241f575d79429a0e5e9cf88ae0605cc81"}, 815 | {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cb6403ce6d8e047495a701dc7c5bd788add903f8986d523e3e20b98b733e421"}, 816 | {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d06016f7f8625a1825ba3732081d77c94589dca78b7a3fc072194851e88461a4"}, 817 | {file = "psutil-5.9.8-cp36-cp36m-win32.whl", hash = "sha256:7d79560ad97af658a0f6adfef8b834b53f64746d45b403f225b85c5c2c140eee"}, 818 | {file = "psutil-5.9.8-cp36-cp36m-win_amd64.whl", hash = "sha256:27cc40c3493bb10de1be4b3f07cae4c010ce715290a5be22b98493509c6299e2"}, 819 | {file = "psutil-5.9.8-cp37-abi3-win32.whl", hash = "sha256:bc56c2a1b0d15aa3eaa5a60c9f3f8e3e565303b465dbf57a1b730e7a2b9844e0"}, 820 | {file = "psutil-5.9.8-cp37-abi3-win_amd64.whl", hash = "sha256:8db4c1b57507eef143a15a6884ca10f7c73876cdf5d51e713151c1236a0e68cf"}, 821 | {file = "psutil-5.9.8-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d16bbddf0693323b8c6123dd804100241da461e41d6e332fb0ba6058f630f8c8"}, 822 | {file = "psutil-5.9.8.tar.gz", hash = "sha256:6be126e3225486dff286a8fb9a06246a5253f4c7c53b475ea5f5ac934e64194c"}, 823 | ] 824 | 825 | [package.extras] 826 | test = ["enum34 ; python_version <= \"3.4\"", "ipaddress ; python_version < \"3.0\"", "mock ; python_version < \"3.0\"", "pywin32 ; sys_platform == \"win32\"", "wmi ; sys_platform == \"win32\""] 827 | 828 | [[package]] 829 | name = "python-dotenv" 830 | version = "0.21.1" 831 | description = "Read key-value pairs from a .env file and set them as environment variables" 832 | optional = false 833 | python-versions = ">=3.7" 834 | groups = ["main"] 835 | files = [ 836 | {file = "python-dotenv-0.21.1.tar.gz", hash = "sha256:1c93de8f636cde3ce377292818d0e440b6e45a82f215c3744979151fa8151c49"}, 837 | {file = "python_dotenv-0.21.1-py3-none-any.whl", hash = "sha256:41e12e0318bebc859fcc4d97d4db8d20ad21721a6aa5047dd59f090391cb549a"}, 838 | ] 839 | 840 | [package.extras] 841 | cli = ["click (>=5.0)"] 842 | 843 | [[package]] 844 | name = "pyyaml" 845 | version = "6.0.2" 846 | description = "YAML parser and emitter for Python" 847 | optional = false 848 | python-versions = ">=3.8" 849 | groups = ["dev"] 850 | files = [ 851 | {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, 852 | {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, 853 | {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, 854 | {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, 855 | {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, 856 | {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, 857 | {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, 858 | {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, 859 | {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, 860 | {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, 861 | {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, 862 | {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, 863 | {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, 864 | {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, 865 | {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, 866 | {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, 867 | {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, 868 | {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, 869 | {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, 870 | {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, 871 | {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, 872 | {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, 873 | {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, 874 | {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, 875 | {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, 876 | {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, 877 | {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, 878 | {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, 879 | {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, 880 | {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, 881 | {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, 882 | {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, 883 | {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, 884 | {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, 885 | {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, 886 | {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, 887 | {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, 888 | {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, 889 | {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, 890 | {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, 891 | {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, 892 | {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, 893 | {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, 894 | {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, 895 | {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, 896 | {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, 897 | {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, 898 | {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, 899 | {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, 900 | {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, 901 | {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, 902 | {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, 903 | {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, 904 | ] 905 | 906 | [[package]] 907 | name = "redis" 908 | version = "7.1.0" 909 | description = "Python client for Redis database and key-value store" 910 | optional = false 911 | python-versions = ">=3.10" 912 | groups = ["main"] 913 | files = [ 914 | {file = "redis-7.1.0-py3-none-any.whl", hash = "sha256:23c52b208f92b56103e17c5d06bdc1a6c2c0b3106583985a76a18f83b265de2b"}, 915 | {file = "redis-7.1.0.tar.gz", hash = "sha256:b1cc3cfa5a2cb9c2ab3ba700864fb0ad75617b41f01352ce5779dabf6d5f9c3c"}, 916 | ] 917 | 918 | [package.dependencies] 919 | async-timeout = {version = ">=4.0.3", markers = "python_full_version < \"3.11.3\""} 920 | 921 | [package.extras] 922 | circuit-breaker = ["pybreaker (>=1.4.0)"] 923 | hiredis = ["hiredis (>=3.2.0)"] 924 | jwt = ["pyjwt (>=2.9.0)"] 925 | ocsp = ["cryptography (>=36.0.1)", "pyopenssl (>=20.0.1)", "requests (>=2.31.0)"] 926 | 927 | [[package]] 928 | name = "sentry-sdk" 929 | version = "1.45.1" 930 | description = "Python client for Sentry (https://sentry.io)" 931 | optional = false 932 | python-versions = "*" 933 | groups = ["main"] 934 | files = [ 935 | {file = "sentry_sdk-1.45.1-py2.py3-none-any.whl", hash = "sha256:608887855ccfe39032bfd03936e3a1c4f4fc99b3a4ac49ced54a4220de61c9c1"}, 936 | {file = "sentry_sdk-1.45.1.tar.gz", hash = "sha256:a16c997c0f4e3df63c0fc5e4207ccb1ab37900433e0f72fef88315d317829a26"}, 937 | ] 938 | 939 | [package.dependencies] 940 | certifi = "*" 941 | urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""} 942 | 943 | [package.extras] 944 | aiohttp = ["aiohttp (>=3.5)"] 945 | arq = ["arq (>=0.23)"] 946 | asyncpg = ["asyncpg (>=0.23)"] 947 | beam = ["apache-beam (>=2.12)"] 948 | bottle = ["bottle (>=0.12.13)"] 949 | celery = ["celery (>=3)"] 950 | celery-redbeat = ["celery-redbeat (>=2)"] 951 | chalice = ["chalice (>=1.16.0)"] 952 | clickhouse-driver = ["clickhouse-driver (>=0.2.0)"] 953 | django = ["django (>=1.8)"] 954 | falcon = ["falcon (>=1.4)"] 955 | fastapi = ["fastapi (>=0.79.0)"] 956 | flask = ["blinker (>=1.1)", "flask (>=0.11)", "markupsafe"] 957 | grpcio = ["grpcio (>=1.21.1)"] 958 | httpx = ["httpx (>=0.16.0)"] 959 | huey = ["huey (>=2)"] 960 | loguru = ["loguru (>=0.5)"] 961 | openai = ["openai (>=1.0.0)", "tiktoken (>=0.3.0)"] 962 | opentelemetry = ["opentelemetry-distro (>=0.35b0)"] 963 | opentelemetry-experimental = ["opentelemetry-distro (>=0.40b0,<1.0)", "opentelemetry-instrumentation-aiohttp-client (>=0.40b0,<1.0)", "opentelemetry-instrumentation-django (>=0.40b0,<1.0)", "opentelemetry-instrumentation-fastapi (>=0.40b0,<1.0)", "opentelemetry-instrumentation-flask (>=0.40b0,<1.0)", "opentelemetry-instrumentation-requests (>=0.40b0,<1.0)", "opentelemetry-instrumentation-sqlite3 (>=0.40b0,<1.0)", "opentelemetry-instrumentation-urllib (>=0.40b0,<1.0)"] 964 | pure-eval = ["asttokens", "executing", "pure-eval"] 965 | pymongo = ["pymongo (>=3.1)"] 966 | pyspark = ["pyspark (>=2.4.4)"] 967 | quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] 968 | rq = ["rq (>=0.6)"] 969 | sanic = ["sanic (>=0.8)"] 970 | sqlalchemy = ["sqlalchemy (>=1.2)"] 971 | starlette = ["starlette (>=0.19.1)"] 972 | starlite = ["starlite (>=1.48)"] 973 | tornado = ["tornado (>=5)"] 974 | 975 | [[package]] 976 | name = "setuptools" 977 | version = "80.1.0" 978 | description = "Easily download, build, install, upgrade, and uninstall Python packages" 979 | optional = false 980 | python-versions = ">=3.9" 981 | groups = ["main"] 982 | files = [ 983 | {file = "setuptools-80.1.0-py3-none-any.whl", hash = "sha256:ea0e7655c05b74819f82e76e11a85b31779fee7c4969e82f72bab0664e8317e4"}, 984 | {file = "setuptools-80.1.0.tar.gz", hash = "sha256:2e308396e1d83de287ada2c2fd6e64286008fe6aca5008e0b6a8cb0e2c86eedd"}, 985 | ] 986 | 987 | [package.extras] 988 | check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.8.0) ; sys_platform != \"cygwin\""] 989 | core = ["importlib_metadata (>=6) ; python_version < \"3.10\"", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"] 990 | cover = ["pytest-cov"] 991 | doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] 992 | enabler = ["pytest-enabler (>=2.2)"] 993 | test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] 994 | type = ["importlib_metadata (>=7.0.2) ; python_version < \"3.10\"", "jaraco.develop (>=7.21) ; sys_platform != \"cygwin\"", "mypy (==1.14.*)", "pytest-mypy"] 995 | 996 | [[package]] 997 | name = "taskipy" 998 | version = "1.10.1" 999 | description = "tasks runner for python projects" 1000 | optional = false 1001 | python-versions = ">=3.6,<4.0" 1002 | groups = ["main"] 1003 | files = [ 1004 | {file = "taskipy-1.10.1-py3-none-any.whl", hash = "sha256:9b38333654da487b6d16de6fa330b7629d1935d1e74819ba4c5f17a1c372d37b"}, 1005 | {file = "taskipy-1.10.1.tar.gz", hash = "sha256:6fa0b11c43d103e376063e90be31d87b435aad50fb7dc1c9a2de9b60a85015ed"}, 1006 | ] 1007 | 1008 | [package.dependencies] 1009 | colorama = ">=0.4.4,<0.5.0" 1010 | mslex = {version = ">=0.3.0,<0.4.0", markers = "sys_platform == \"win32\""} 1011 | psutil = ">=5.7.2,<6.0.0" 1012 | tomli = ">=1.2.3,<2.0.0" 1013 | 1014 | [[package]] 1015 | name = "tomli" 1016 | version = "1.2.3" 1017 | description = "A lil' TOML parser" 1018 | optional = false 1019 | python-versions = ">=3.6" 1020 | groups = ["main", "dev"] 1021 | files = [ 1022 | {file = "tomli-1.2.3-py3-none-any.whl", hash = "sha256:e3069e4be3ead9668e21cb9b074cd948f7b3113fd9c8bba083f48247aab8b11c"}, 1023 | {file = "tomli-1.2.3.tar.gz", hash = "sha256:05b6166bff487dc068d322585c7ea4ef78deed501cc124060e0f238e89a9231f"}, 1024 | ] 1025 | 1026 | [[package]] 1027 | name = "typing-extensions" 1028 | version = "4.13.2" 1029 | description = "Backported and Experimental Type Hints for Python 3.8+" 1030 | optional = false 1031 | python-versions = ">=3.8" 1032 | groups = ["main", "dev"] 1033 | files = [ 1034 | {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, 1035 | {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, 1036 | ] 1037 | markers = {main = "python_version == \"3.10\""} 1038 | 1039 | [[package]] 1040 | name = "urllib3" 1041 | version = "2.4.0" 1042 | description = "HTTP library with thread-safe connection pooling, file post, and more." 1043 | optional = false 1044 | python-versions = ">=3.9" 1045 | groups = ["main"] 1046 | files = [ 1047 | {file = "urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813"}, 1048 | {file = "urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466"}, 1049 | ] 1050 | 1051 | [package.extras] 1052 | brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] 1053 | h2 = ["h2 (>=4,<5)"] 1054 | socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] 1055 | zstd = ["zstandard (>=0.18.0)"] 1056 | 1057 | [[package]] 1058 | name = "virtualenv" 1059 | version = "20.30.0" 1060 | description = "Virtual Python Environment builder" 1061 | optional = false 1062 | python-versions = ">=3.8" 1063 | groups = ["dev"] 1064 | files = [ 1065 | {file = "virtualenv-20.30.0-py3-none-any.whl", hash = "sha256:e34302959180fca3af42d1800df014b35019490b119eba981af27f2fa486e5d6"}, 1066 | {file = "virtualenv-20.30.0.tar.gz", hash = "sha256:800863162bcaa5450a6e4d721049730e7f2dae07720e0902b0e4040bd6f9ada8"}, 1067 | ] 1068 | 1069 | [package.dependencies] 1070 | distlib = ">=0.3.7,<1" 1071 | filelock = ">=3.12.2,<4" 1072 | platformdirs = ">=3.9.1,<5" 1073 | 1074 | [package.extras] 1075 | docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] 1076 | test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"GraalVM\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""] 1077 | 1078 | [[package]] 1079 | name = "win32-setctime" 1080 | version = "1.2.0" 1081 | description = "A small Python utility to set file creation time on Windows" 1082 | optional = false 1083 | python-versions = ">=3.5" 1084 | groups = ["main"] 1085 | markers = "sys_platform == \"win32\"" 1086 | files = [ 1087 | {file = "win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390"}, 1088 | {file = "win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0"}, 1089 | ] 1090 | 1091 | [package.extras] 1092 | dev = ["black (>=19.3b0) ; python_version >= \"3.6\"", "pytest (>=4.6.2)"] 1093 | 1094 | [[package]] 1095 | name = "yarl" 1096 | version = "1.20.0" 1097 | description = "Yet another URL library" 1098 | optional = false 1099 | python-versions = ">=3.9" 1100 | groups = ["main"] 1101 | files = [ 1102 | {file = "yarl-1.20.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f1f6670b9ae3daedb325fa55fbe31c22c8228f6e0b513772c2e1c623caa6ab22"}, 1103 | {file = "yarl-1.20.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:85a231fa250dfa3308f3c7896cc007a47bc76e9e8e8595c20b7426cac4884c62"}, 1104 | {file = "yarl-1.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1a06701b647c9939d7019acdfa7ebbfbb78ba6aa05985bb195ad716ea759a569"}, 1105 | {file = "yarl-1.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7595498d085becc8fb9203aa314b136ab0516c7abd97e7d74f7bb4eb95042abe"}, 1106 | {file = "yarl-1.20.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:af5607159085dcdb055d5678fc2d34949bd75ae6ea6b4381e784bbab1c3aa195"}, 1107 | {file = "yarl-1.20.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:95b50910e496567434cb77a577493c26bce0f31c8a305135f3bda6a2483b8e10"}, 1108 | {file = "yarl-1.20.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b594113a301ad537766b4e16a5a6750fcbb1497dcc1bc8a4daae889e6402a634"}, 1109 | {file = "yarl-1.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:083ce0393ea173cd37834eb84df15b6853b555d20c52703e21fbababa8c129d2"}, 1110 | {file = "yarl-1.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f1a350a652bbbe12f666109fbddfdf049b3ff43696d18c9ab1531fbba1c977a"}, 1111 | {file = "yarl-1.20.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fb0caeac4a164aadce342f1597297ec0ce261ec4532bbc5a9ca8da5622f53867"}, 1112 | {file = "yarl-1.20.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:d88cc43e923f324203f6ec14434fa33b85c06d18d59c167a0637164863b8e995"}, 1113 | {file = "yarl-1.20.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e52d6ed9ea8fd3abf4031325dc714aed5afcbfa19ee4a89898d663c9976eb487"}, 1114 | {file = "yarl-1.20.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ce360ae48a5e9961d0c730cf891d40698a82804e85f6e74658fb175207a77cb2"}, 1115 | {file = "yarl-1.20.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:06d06c9d5b5bc3eb56542ceeba6658d31f54cf401e8468512447834856fb0e61"}, 1116 | {file = "yarl-1.20.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c27d98f4e5c4060582f44e58309c1e55134880558f1add7a87c1bc36ecfade19"}, 1117 | {file = "yarl-1.20.0-cp310-cp310-win32.whl", hash = "sha256:f4d3fa9b9f013f7050326e165c3279e22850d02ae544ace285674cb6174b5d6d"}, 1118 | {file = "yarl-1.20.0-cp310-cp310-win_amd64.whl", hash = "sha256:bc906b636239631d42eb8a07df8359905da02704a868983265603887ed68c076"}, 1119 | {file = "yarl-1.20.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fdb5204d17cb32b2de2d1e21c7461cabfacf17f3645e4b9039f210c5d3378bf3"}, 1120 | {file = "yarl-1.20.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:eaddd7804d8e77d67c28d154ae5fab203163bd0998769569861258e525039d2a"}, 1121 | {file = "yarl-1.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:634b7ba6b4a85cf67e9df7c13a7fb2e44fa37b5d34501038d174a63eaac25ee2"}, 1122 | {file = "yarl-1.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d409e321e4addf7d97ee84162538c7258e53792eb7c6defd0c33647d754172e"}, 1123 | {file = "yarl-1.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ea52f7328a36960ba3231c6677380fa67811b414798a6e071c7085c57b6d20a9"}, 1124 | {file = "yarl-1.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c8703517b924463994c344dcdf99a2d5ce9eca2b6882bb640aa555fb5efc706a"}, 1125 | {file = "yarl-1.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:077989b09ffd2f48fb2d8f6a86c5fef02f63ffe6b1dd4824c76de7bb01e4f2e2"}, 1126 | {file = "yarl-1.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0acfaf1da020253f3533526e8b7dd212838fdc4109959a2c53cafc6db611bff2"}, 1127 | {file = "yarl-1.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b4230ac0b97ec5eeb91d96b324d66060a43fd0d2a9b603e3327ed65f084e41f8"}, 1128 | {file = "yarl-1.20.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a6a1e6ae21cdd84011c24c78d7a126425148b24d437b5702328e4ba640a8902"}, 1129 | {file = "yarl-1.20.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:86de313371ec04dd2531f30bc41a5a1a96f25a02823558ee0f2af0beaa7ca791"}, 1130 | {file = "yarl-1.20.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:dd59c9dd58ae16eaa0f48c3d0cbe6be8ab4dc7247c3ff7db678edecbaf59327f"}, 1131 | {file = "yarl-1.20.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a0bc5e05f457b7c1994cc29e83b58f540b76234ba6b9648a4971ddc7f6aa52da"}, 1132 | {file = "yarl-1.20.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c9471ca18e6aeb0e03276b5e9b27b14a54c052d370a9c0c04a68cefbd1455eb4"}, 1133 | {file = "yarl-1.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:40ed574b4df723583a26c04b298b283ff171bcc387bc34c2683235e2487a65a5"}, 1134 | {file = "yarl-1.20.0-cp311-cp311-win32.whl", hash = "sha256:db243357c6c2bf3cd7e17080034ade668d54ce304d820c2a58514a4e51d0cfd6"}, 1135 | {file = "yarl-1.20.0-cp311-cp311-win_amd64.whl", hash = "sha256:8c12cd754d9dbd14204c328915e23b0c361b88f3cffd124129955e60a4fbfcfb"}, 1136 | {file = "yarl-1.20.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e06b9f6cdd772f9b665e5ba8161968e11e403774114420737f7884b5bd7bdf6f"}, 1137 | {file = "yarl-1.20.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b9ae2fbe54d859b3ade40290f60fe40e7f969d83d482e84d2c31b9bff03e359e"}, 1138 | {file = "yarl-1.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6d12b8945250d80c67688602c891237994d203d42427cb14e36d1a732eda480e"}, 1139 | {file = "yarl-1.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:087e9731884621b162a3e06dc0d2d626e1542a617f65ba7cc7aeab279d55ad33"}, 1140 | {file = "yarl-1.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:69df35468b66c1a6e6556248e6443ef0ec5f11a7a4428cf1f6281f1879220f58"}, 1141 | {file = "yarl-1.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b2992fe29002fd0d4cbaea9428b09af9b8686a9024c840b8a2b8f4ea4abc16f"}, 1142 | {file = "yarl-1.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4c903e0b42aab48abfbac668b5a9d7b6938e721a6341751331bcd7553de2dcae"}, 1143 | {file = "yarl-1.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf099e2432131093cc611623e0b0bcc399b8cddd9a91eded8bfb50402ec35018"}, 1144 | {file = "yarl-1.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a7f62f5dc70a6c763bec9ebf922be52aa22863d9496a9a30124d65b489ea672"}, 1145 | {file = "yarl-1.20.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:54ac15a8b60382b2bcefd9a289ee26dc0920cf59b05368c9b2b72450751c6eb8"}, 1146 | {file = "yarl-1.20.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:25b3bc0763a7aca16a0f1b5e8ef0f23829df11fb539a1b70476dcab28bd83da7"}, 1147 | {file = "yarl-1.20.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b2586e36dc070fc8fad6270f93242124df68b379c3a251af534030a4a33ef594"}, 1148 | {file = "yarl-1.20.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:866349da9d8c5290cfefb7fcc47721e94de3f315433613e01b435473be63daa6"}, 1149 | {file = "yarl-1.20.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:33bb660b390a0554d41f8ebec5cd4475502d84104b27e9b42f5321c5192bfcd1"}, 1150 | {file = "yarl-1.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:737e9f171e5a07031cbee5e9180f6ce21a6c599b9d4b2c24d35df20a52fabf4b"}, 1151 | {file = "yarl-1.20.0-cp312-cp312-win32.whl", hash = "sha256:839de4c574169b6598d47ad61534e6981979ca2c820ccb77bf70f4311dd2cc64"}, 1152 | {file = "yarl-1.20.0-cp312-cp312-win_amd64.whl", hash = "sha256:3d7dbbe44b443b0c4aa0971cb07dcb2c2060e4a9bf8d1301140a33a93c98e18c"}, 1153 | {file = "yarl-1.20.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2137810a20b933b1b1b7e5cf06a64c3ed3b4747b0e5d79c9447c00db0e2f752f"}, 1154 | {file = "yarl-1.20.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:447c5eadd750db8389804030d15f43d30435ed47af1313303ed82a62388176d3"}, 1155 | {file = "yarl-1.20.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:42fbe577272c203528d402eec8bf4b2d14fd49ecfec92272334270b850e9cd7d"}, 1156 | {file = "yarl-1.20.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18e321617de4ab170226cd15006a565d0fa0d908f11f724a2c9142d6b2812ab0"}, 1157 | {file = "yarl-1.20.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4345f58719825bba29895011e8e3b545e6e00257abb984f9f27fe923afca2501"}, 1158 | {file = "yarl-1.20.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d9b980d7234614bc4674468ab173ed77d678349c860c3af83b1fffb6a837ddc"}, 1159 | {file = "yarl-1.20.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af4baa8a445977831cbaa91a9a84cc09debb10bc8391f128da2f7bd070fc351d"}, 1160 | {file = "yarl-1.20.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:123393db7420e71d6ce40d24885a9e65eb1edefc7a5228db2d62bcab3386a5c0"}, 1161 | {file = "yarl-1.20.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ab47acc9332f3de1b39e9b702d9c916af7f02656b2a86a474d9db4e53ef8fd7a"}, 1162 | {file = "yarl-1.20.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4a34c52ed158f89876cba9c600b2c964dfc1ca52ba7b3ab6deb722d1d8be6df2"}, 1163 | {file = "yarl-1.20.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:04d8cfb12714158abf2618f792c77bc5c3d8c5f37353e79509608be4f18705c9"}, 1164 | {file = "yarl-1.20.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7dc63ad0d541c38b6ae2255aaa794434293964677d5c1ec5d0116b0e308031f5"}, 1165 | {file = "yarl-1.20.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d02b591a64e4e6ca18c5e3d925f11b559c763b950184a64cf47d74d7e41877"}, 1166 | {file = "yarl-1.20.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:95fc9876f917cac7f757df80a5dda9de59d423568460fe75d128c813b9af558e"}, 1167 | {file = "yarl-1.20.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bb769ae5760cd1c6a712135ee7915f9d43f11d9ef769cb3f75a23e398a92d384"}, 1168 | {file = "yarl-1.20.0-cp313-cp313-win32.whl", hash = "sha256:70e0c580a0292c7414a1cead1e076c9786f685c1fc4757573d2967689b370e62"}, 1169 | {file = "yarl-1.20.0-cp313-cp313-win_amd64.whl", hash = "sha256:4c43030e4b0af775a85be1fa0433119b1565673266a70bf87ef68a9d5ba3174c"}, 1170 | {file = "yarl-1.20.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b6c4c3d0d6a0ae9b281e492b1465c72de433b782e6b5001c8e7249e085b69051"}, 1171 | {file = "yarl-1.20.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8681700f4e4df891eafa4f69a439a6e7d480d64e52bf460918f58e443bd3da7d"}, 1172 | {file = "yarl-1.20.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:84aeb556cb06c00652dbf87c17838eb6d92cfd317799a8092cee0e570ee11229"}, 1173 | {file = "yarl-1.20.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f166eafa78810ddb383e930d62e623d288fb04ec566d1b4790099ae0f31485f1"}, 1174 | {file = "yarl-1.20.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5d3d6d14754aefc7a458261027a562f024d4f6b8a798adb472277f675857b1eb"}, 1175 | {file = "yarl-1.20.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2a8f64df8ed5d04c51260dbae3cc82e5649834eebea9eadfd829837b8093eb00"}, 1176 | {file = "yarl-1.20.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4d9949eaf05b4d30e93e4034a7790634bbb41b8be2d07edd26754f2e38e491de"}, 1177 | {file = "yarl-1.20.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c366b254082d21cc4f08f522ac201d0d83a8b8447ab562732931d31d80eb2a5"}, 1178 | {file = "yarl-1.20.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:91bc450c80a2e9685b10e34e41aef3d44ddf99b3a498717938926d05ca493f6a"}, 1179 | {file = "yarl-1.20.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9c2aa4387de4bc3a5fe158080757748d16567119bef215bec643716b4fbf53f9"}, 1180 | {file = "yarl-1.20.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:d2cbca6760a541189cf87ee54ff891e1d9ea6406079c66341008f7ef6ab61145"}, 1181 | {file = "yarl-1.20.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:798a5074e656f06b9fad1a162be5a32da45237ce19d07884d0b67a0aa9d5fdda"}, 1182 | {file = "yarl-1.20.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:f106e75c454288472dbe615accef8248c686958c2e7dd3b8d8ee2669770d020f"}, 1183 | {file = "yarl-1.20.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:3b60a86551669c23dc5445010534d2c5d8a4e012163218fc9114e857c0586fdd"}, 1184 | {file = "yarl-1.20.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:3e429857e341d5e8e15806118e0294f8073ba9c4580637e59ab7b238afca836f"}, 1185 | {file = "yarl-1.20.0-cp313-cp313t-win32.whl", hash = "sha256:65a4053580fe88a63e8e4056b427224cd01edfb5f951498bfefca4052f0ce0ac"}, 1186 | {file = "yarl-1.20.0-cp313-cp313t-win_amd64.whl", hash = "sha256:53b2da3a6ca0a541c1ae799c349788d480e5144cac47dba0266c7cb6c76151fe"}, 1187 | {file = "yarl-1.20.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:119bca25e63a7725b0c9d20ac67ca6d98fa40e5a894bd5d4686010ff73397914"}, 1188 | {file = "yarl-1.20.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:35d20fb919546995f1d8c9e41f485febd266f60e55383090010f272aca93edcc"}, 1189 | {file = "yarl-1.20.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:484e7a08f72683c0f160270566b4395ea5412b4359772b98659921411d32ad26"}, 1190 | {file = "yarl-1.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d8a3d54a090e0fff5837cd3cc305dd8a07d3435a088ddb1f65e33b322f66a94"}, 1191 | {file = "yarl-1.20.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f0cf05ae2d3d87a8c9022f3885ac6dea2b751aefd66a4f200e408a61ae9b7f0d"}, 1192 | {file = "yarl-1.20.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a884b8974729e3899d9287df46f015ce53f7282d8d3340fa0ed57536b440621c"}, 1193 | {file = "yarl-1.20.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f8d8aa8dd89ffb9a831fedbcb27d00ffd9f4842107d52dc9d57e64cb34073d5c"}, 1194 | {file = "yarl-1.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b4e88d6c3c8672f45a30867817e4537df1bbc6f882a91581faf1f6d9f0f1b5a"}, 1195 | {file = "yarl-1.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdb77efde644d6f1ad27be8a5d67c10b7f769804fff7a966ccb1da5a4de4b656"}, 1196 | {file = "yarl-1.20.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4ba5e59f14bfe8d261a654278a0f6364feef64a794bd456a8c9e823071e5061c"}, 1197 | {file = "yarl-1.20.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:d0bf955b96ea44ad914bc792c26a0edcd71b4668b93cbcd60f5b0aeaaed06c64"}, 1198 | {file = "yarl-1.20.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:27359776bc359ee6eaefe40cb19060238f31228799e43ebd3884e9c589e63b20"}, 1199 | {file = "yarl-1.20.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:04d9c7a1dc0a26efb33e1acb56c8849bd57a693b85f44774356c92d610369efa"}, 1200 | {file = "yarl-1.20.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:faa709b66ae0e24c8e5134033187a972d849d87ed0a12a0366bedcc6b5dc14a5"}, 1201 | {file = "yarl-1.20.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:44869ee8538208fe5d9342ed62c11cc6a7a1af1b3d0bb79bb795101b6e77f6e0"}, 1202 | {file = "yarl-1.20.0-cp39-cp39-win32.whl", hash = "sha256:b7fa0cb9fd27ffb1211cde944b41f5c67ab1c13a13ebafe470b1e206b8459da8"}, 1203 | {file = "yarl-1.20.0-cp39-cp39-win_amd64.whl", hash = "sha256:d4fad6e5189c847820288286732075f213eabf81be4d08d6cc309912e62be5b7"}, 1204 | {file = "yarl-1.20.0-py3-none-any.whl", hash = "sha256:5d0fe6af927a47a230f31e6004621fd0959eaa915fc62acfafa67ff7229a3124"}, 1205 | {file = "yarl-1.20.0.tar.gz", hash = "sha256:686d51e51ee5dfe62dec86e4866ee0e9ed66df700d55c828a615640adc885307"}, 1206 | ] 1207 | 1208 | [package.dependencies] 1209 | idna = ">=2.0" 1210 | multidict = ">=4.0" 1211 | propcache = ">=0.2.1" 1212 | 1213 | [metadata] 1214 | lock-version = "2.1" 1215 | python-versions = "^3.10" 1216 | content-hash = "678a1f25ee2ce8b3ddc9453d45ac12b475b98290eb62c8fe79cb26fb8d701c1e" 1217 | --------------------------------------------------------------------------------