├── .gitignore ├── .readthedocs.yaml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── discord └── ext │ └── lava │ ├── __init__.py │ ├── _backoff.py │ ├── _utilities.py │ ├── enums.py │ ├── exceptions.py │ ├── link.py │ ├── objects │ ├── __init__.py │ ├── events.py │ ├── filters.py │ ├── playlist.py │ ├── result.py │ ├── stats.py │ └── track.py │ ├── player.py │ ├── py.typed │ └── types │ ├── common.py │ ├── objects │ ├── events.py │ ├── filters.py │ ├── playlist.py │ ├── stats.py │ └── track.py │ ├── rest.py │ └── websocket.py ├── docs ├── Makefile ├── conf.py ├── extensions │ └── resource_links.py ├── index.rst ├── make.bat ├── pages │ ├── contribution.rst │ ├── installation.rst │ ├── reference.rst │ └── usage.rst └── static │ └── custom.css ├── poetry.lock └── pyproject.toml /.gitignore: -------------------------------------------------------------------------------- 1 | # ide 2 | .idea/ 3 | 4 | # python 5 | *.pyc 6 | 7 | # docs 8 | build/ 9 | _build/ 10 | 11 | # lavalink 12 | Lavalink.jar 13 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | build: 4 | os: ubuntu-22.04 5 | tools: 6 | python: "3.11" 7 | jobs: 8 | post_install: 9 | - pip install poetry 10 | - poetry config virtualenvs.create false 11 | - poetry install --with docs 12 | 13 | sphinx: 14 | configuration: docs/conf.py 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # v1.0.0a1 2 | 3 | ### New Features 4 | 5 | - n/a 6 | 7 | ### Changes 8 | 9 | - n/a 10 | 11 | ### Bug Fixes 12 | 13 | - n/a 14 | 15 | ### Notes 16 | 17 | - n/a 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-present Aaron Hennessey (aaronhnsy) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # discord-ext-lava 2 | 3 | A [discord.py](https://github.com/Rapptz/discord.py) extension for the [Lavalink](https://github.com/lavalink-devs/Lavalink) API and WebSocket. 4 | 5 | ## Installation 6 | 7 | ```shell 8 | pip install discord-ext-lava 9 | ``` 10 | 11 | ## Support 12 | 13 | - [Documentation](https://discord-ext-lava.readthedocs.io/) 14 | - [GitHub](https://github.com/aaronhnsy/discord-ext-lava) 15 | -------------------------------------------------------------------------------- /discord/ext/lava/__init__.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from typing import Literal, NamedTuple 3 | 4 | from .enums import * 5 | from .exceptions import * 6 | from .link import * 7 | from .objects import * 8 | from .player import * 9 | 10 | 11 | class VersionInfo(NamedTuple): 12 | major: int 13 | minor: int 14 | micro: int 15 | releaselevel: Literal["alpha", "beta", "candidate", "final"] 16 | serial: int 17 | 18 | 19 | __version_info__: VersionInfo = VersionInfo(major=1, minor=0, micro=0, releaselevel="alpha", serial=1) 20 | __version__: str = "1.0.0a1" 21 | 22 | __title__: str = "discord-ext-lava" 23 | __url__: str = "https://github.com/aaronhnsy/discord-ext-lava" 24 | 25 | __author__: str = "Aaron Hennessey (aaronhnsy)" 26 | __email__: str = "aaronhnsy@gmail.com" 27 | 28 | __license__: str = "MIT" 29 | __copyright__: str = "Copyright (c) 2019-present Aaron Hennessey (aaronhnsy)" 30 | 31 | logging.getLogger("discord.ext.lava") 32 | -------------------------------------------------------------------------------- /discord/ext/lava/_backoff.py: -------------------------------------------------------------------------------- 1 | """ 2 | https://github.com/PythonistaGuild/Wavelink/blob/fe27c9175e03ce42ea55ad47a4cb7b02bd1324d7/wavelink/backoff.py#L29-L75 3 | 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2021 PythonistaGuild, EvieePy, Rapptz 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a 9 | copy of this software and associated documentation files (the "Software"), 10 | to deal in the Software without restriction, including without limitation 11 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 12 | and/or sell copies of the Software, and to permit persons to whom the 13 | Software is furnished to do so, subject to the following conditions: 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | OR 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 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | DEALINGS IN THE SOFTWARE. 23 | """ 24 | 25 | import random 26 | from collections.abc import Callable 27 | 28 | 29 | class Backoff: 30 | 31 | def __init__( 32 | self, 33 | *, 34 | base: int = 1, 35 | max_time: float, 36 | max_tries: int | None 37 | ) -> None: 38 | 39 | self.base: int = base 40 | self.max_wait: float = max_time 41 | self.max_tries: int | None = max_tries 42 | 43 | self.tries: int = 0 44 | self.last_wait: float = 0 45 | 46 | _random = random.Random() 47 | _random.seed() 48 | self.uniform: Callable[[float, float], float] = _random.uniform 49 | 50 | def reset(self) -> None: 51 | self.tries = 0 52 | 53 | def calculate(self) -> float: 54 | 55 | self.tries += 1 56 | 57 | exponent = min((self.tries ** 2), self.max_wait) 58 | wait = self.uniform(0, (self.base * 2) * exponent) 59 | 60 | if wait <= self.last_wait: 61 | wait = self.last_wait * 2 62 | 63 | self.last_wait = wait 64 | wait = min(wait, self.max_wait) 65 | 66 | if self.max_tries and self.tries > self.max_tries: 67 | self.tries = 0 68 | self.last_wait = 0 69 | 70 | return wait 71 | -------------------------------------------------------------------------------- /discord/ext/lava/_utilities.py: -------------------------------------------------------------------------------- 1 | import functools 2 | import itertools 3 | import re 4 | from collections.abc import Callable, Iterable 5 | from typing import Any 6 | 7 | import aiohttp 8 | import discord.utils 9 | 10 | from .types.common import JSON, JSONLoads 11 | 12 | 13 | def ordinal(number: int) -> str: 14 | return "%d%s" % (number, "tsnrhtdd"[(number / 10 % 10 != 1) * (number % 10 < 4) * number % 10::4]) 15 | 16 | 17 | def chunks[T](iterable: Iterable[T], n: int) -> Iterable[tuple[T, ...]]: 18 | it = iter(iterable) 19 | return iter(lambda: tuple(itertools.islice(it, n)), ()) 20 | 21 | 22 | PASCAL_CASE_TO_SNAKE_CASE_REGEX: re.Pattern[str] = re.compile(r"(? str: 26 | snake_case = re.sub(PASCAL_CASE_TO_SNAKE_CASE_REGEX, "_", event_type) 27 | return f"lava_{snake_case.lower().replace("_event", "")}" 28 | 29 | 30 | async def json_or_text(response: aiohttp.ClientResponse, json_loads: JSONLoads) -> JSON | str: 31 | text = await response.text(encoding="utf-8") 32 | if response.headers.get("Content-Type") in ["application/json", "application/json; charset=utf-8"]: 33 | return json_loads(text) 34 | return text 35 | 36 | 37 | class DeferredMessage: 38 | 39 | def __init__[**P](self, callable: Callable[P, str], *args: P.args, **kwargs: P.kwargs) -> None: 40 | self.callable: functools.partial[str] = functools.partial(callable, *args, **kwargs) 41 | 42 | def __str__(self) -> str: 43 | return f"{self.callable()}" 44 | 45 | 46 | SPOTIFY_REGEX: re.Pattern[str] = re.compile( 47 | r"(https?://open.)?" 48 | r"(spotify)" 49 | r"(.com/|:)" 50 | r"(?Palbum|playlist|artist|track)" 51 | r"([/:])" 52 | r"(?P[a-zA-Z0-9]+)" 53 | r"([? &]si = [a-zA-Z0-9]+)?" 54 | r"([? &]dl_branch=[0-9]+)?" 55 | ) 56 | 57 | MISSING: Any = discord.utils.MISSING 58 | -------------------------------------------------------------------------------- /discord/ext/lava/enums.py: -------------------------------------------------------------------------------- 1 | import enum 2 | 3 | 4 | __all__ = [ 5 | "TrackEndReason", 6 | "ExceptionSeverity", 7 | ] 8 | 9 | 10 | class TrackEndReason(enum.Enum): 11 | FINISHED = "finished" 12 | LOAD_FAILED = "loadFailed" 13 | STOPPED = "stopped" 14 | REPLACED = "replaced" 15 | CLEANUP = "cleanup" 16 | 17 | 18 | class ExceptionSeverity(enum.Enum): 19 | COMMON = "common" 20 | SUSPICIOUS = "suspicious" 21 | FATAL = "fatal" 22 | -------------------------------------------------------------------------------- /discord/ext/lava/exceptions.py: -------------------------------------------------------------------------------- 1 | from .enums import ExceptionSeverity 2 | from .types.common import ExceptionData 3 | 4 | 5 | __all__ = [ 6 | "LavaError", 7 | "LinkAlreadyConnected", 8 | "LinkConnectionError", 9 | "LinkNotReady", 10 | "PlayerError", 11 | "PlayerAlreadyConnected", 12 | "SearchError", 13 | "SearchFailed", 14 | "NoSearchResults", 15 | ] 16 | 17 | 18 | class LavaError(Exception): 19 | pass 20 | 21 | 22 | class LinkAlreadyConnected(LavaError): 23 | pass 24 | 25 | 26 | class LinkConnectionError(LavaError): 27 | pass 28 | 29 | 30 | class LinkNotReady(LavaError): 31 | pass 32 | 33 | 34 | class PlayerError(LavaError): 35 | pass 36 | 37 | 38 | class PlayerNotConnected(PlayerError): 39 | pass 40 | 41 | 42 | class PlayerAlreadyConnected(PlayerError): 43 | pass 44 | 45 | 46 | class SearchError(LavaError): 47 | pass 48 | 49 | 50 | class SearchFailed(SearchError): 51 | 52 | def __init__(self, exception: ExceptionData) -> None: 53 | self.message: str | None = exception["message"] 54 | self.severity: ExceptionSeverity = ExceptionSeverity(exception["severity"]) 55 | self.cause: str = exception["cause"] 56 | 57 | 58 | class NoSearchResults(SearchError): 59 | 60 | def __init__(self, *, search: str) -> None: 61 | self.search = search 62 | -------------------------------------------------------------------------------- /discord/ext/lava/link.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import itertools 3 | import json as _json 4 | import logging 5 | import random 6 | import string 7 | import traceback 8 | from typing import TYPE_CHECKING, Any, Generic, cast 9 | from typing_extensions import TypeVar 10 | 11 | import aiohttp 12 | import spotipy 13 | 14 | from ._backoff import Backoff 15 | from ._utilities import SPOTIFY_REGEX, DeferredMessage, chunks, json_or_text, ordinal 16 | from .exceptions import LinkAlreadyConnected, LinkConnectionError, NoSearchResults, SearchError, SearchFailed 17 | from .objects.playlist import Playlist 18 | from .objects.result import Result 19 | from .objects.stats import Stats 20 | from .objects.track import Track 21 | from .types.common import JSON, JSONDumps, JSONLoads, SpotifySearchType 22 | from .types.rest import RequestData, RequestKwargs, RequestMethod, RequestParameters, SearchData 23 | from .types.websocket import Payload 24 | 25 | 26 | if TYPE_CHECKING: 27 | from .player import Player # type: ignore 28 | 29 | 30 | __all__ = ["Link"] 31 | 32 | __ws_log__: logging.Logger = logging.getLogger("lava.websocket") 33 | __rest_log__: logging.Logger = logging.getLogger("lava.rest") 34 | 35 | PlayerT = TypeVar("PlayerT", bound="Player", default="Player", covariant=True) 36 | 37 | 38 | class Link(Generic[PlayerT]): 39 | 40 | def __init__( 41 | self, 42 | *, 43 | host: str | None = None, 44 | port: int | None = None, 45 | ws_url: str | None = None, 46 | rest_url: str | None = None, 47 | password: str, 48 | user_id: int, 49 | json_dumps: JSONDumps | None = None, 50 | json_loads: JSONLoads | None = None, 51 | spotify_client_id: str | None = None, 52 | spotify_client_secret: str | None = None, 53 | ) -> None: 54 | if (host is None or port is None) and (ws_url is None or rest_url is None): 55 | raise ValueError( 56 | "You must provide either the `host` and `port` OR `ws_url` and `rest_url` parameters. " 57 | "`ws_url` and `rest_url` will take precedence over `host` and `port` if both sets are provided." 58 | ) 59 | self._host: str | None = host 60 | self._port: int | None = port 61 | self._ws_url: str | None = ws_url 62 | self._rest_url: str | None = rest_url 63 | 64 | self._password: str = password 65 | self._user_id: int = user_id 66 | 67 | self._json_dumps: JSONDumps = json_dumps or _json.dumps 68 | self._json_loads: JSONLoads = json_loads or _json.loads 69 | 70 | self._spotify: spotipy.Client | None = spotipy.Client( 71 | client_id=spotify_client_id, 72 | client_secret=spotify_client_secret, 73 | ) if spotify_client_id and spotify_client_secret else None 74 | 75 | self._backoff: Backoff = Backoff(base=2, max_time=60 * 5, max_tries=5) 76 | self._task: asyncio.Task[None] | None = None 77 | self._session: aiohttp.ClientSession | None = None 78 | self._websocket: aiohttp.ClientWebSocketResponse | None = None 79 | 80 | self._ready_event: asyncio.Event = asyncio.Event() 81 | 82 | self._identifier: str = "".join(random.sample(string.ascii_uppercase, 20)) 83 | self._session_id: str | None = None 84 | self._stats: Stats | None = None 85 | self._players: dict[int, PlayerT] = {} 86 | 87 | def __repr__(self) -> str: 88 | return f"" 89 | 90 | # properties 91 | 92 | @property 93 | def identifier(self) -> str: 94 | return self._identifier 95 | 96 | @property 97 | def session_id(self) -> str | None: 98 | return self._session_id 99 | 100 | @property 101 | def stats(self) -> Stats | None: 102 | return self._stats 103 | 104 | @property 105 | def players(self) -> dict[int, PlayerT]: 106 | return self._players 107 | 108 | # utility methods 109 | 110 | def is_connected(self) -> bool: 111 | return self._websocket is not None and self._websocket.closed is False 112 | 113 | def is_ready(self) -> bool: 114 | return self.is_connected() is True and self.session_id is not None 115 | 116 | # websocket 117 | 118 | async def connect(self) -> None: 119 | if self.is_connected(): 120 | raise LinkAlreadyConnected(f"Link '{self.identifier}' is already connected.") 121 | try: 122 | from . import __version__ 123 | if self._session is None: 124 | self._session = aiohttp.ClientSession() 125 | websocket = await self._session.ws_connect( 126 | url=f"{self._ws_url or f'ws://{self._host}:{self._port}'}/v4/websocket", 127 | headers={ 128 | "Authorization": self._password, 129 | "User-Id": str(self._user_id), 130 | "Client-Name": f"discord-ext-lava/{__version__}" 131 | } 132 | ) 133 | except Exception as error: 134 | if isinstance(error, aiohttp.WSServerHandshakeError): 135 | if error.status == 401: 136 | message = f"Link '{self.identifier}' could not connect to its websocket due to an incorrect " \ 137 | f"password. " 138 | elif error.status in {403, 404}: 139 | message = f"Link '{self.identifier}' could not connect to its websocket, if you're using the " \ 140 | f"`ws_url` parameter please make sure its path is correct." 141 | else: 142 | message = f"Link '{self.identifier}' could not connect to its websocket, encountered a " \ 143 | f"'{error.status}' status code error." 144 | else: 145 | message = f"Link '{self.identifier}' raised '{error.__class__.__name__}' while connecting to its " \ 146 | f"websocket." 147 | __ws_log__.error(message) 148 | raise LinkConnectionError(message) 149 | 150 | self._backoff.reset() 151 | self._websocket = websocket 152 | 153 | if self._task is None or self._task.done() is True: 154 | self._task = asyncio.create_task(self._listen()) 155 | 156 | async def _reset_state(self) -> None: 157 | self._backoff.reset() 158 | 159 | if self._task is not None and self._task.done() is False: 160 | self._task.cancel() 161 | self._task = None 162 | 163 | if self._websocket is not None and self._websocket.closed is False: 164 | await self._websocket.close() 165 | self._websocket = None 166 | 167 | if self._session is not None: 168 | await self._session.close() 169 | self._session = None 170 | 171 | async def _process_payload(self, payload: Payload, /) -> None: 172 | __ws_log__.debug( 173 | f"Link '{self.identifier}' received a '{payload['op']}' payload.\n%s", 174 | DeferredMessage(_json.dumps, payload, indent=4), 175 | ) 176 | match payload["op"]: 177 | case "ready": 178 | self._session_id = payload["sessionId"] 179 | self._ready_event.set() 180 | __ws_log__.info(f"Link '{self.identifier}' is ready.") 181 | case "stats": 182 | self._stats = Stats(payload) 183 | case "event": 184 | if not (player := self._players.get(int(payload["guildId"]))): 185 | __ws_log__.warning( 186 | f"Link '{self.identifier}' received a '{payload['type']}' event for a non-existent player " 187 | f"with id '{payload['guildId']}'." 188 | ) 189 | return 190 | player._dispatch_event(payload) 191 | case "playerUpdate": 192 | if not (player := self._players.get(int(payload["guildId"]))): 193 | __ws_log__.warning( 194 | f"Link '{self.identifier}' received a player update for a non-existent player " 195 | f"with id '{payload['guildId']}'." 196 | ) 197 | return 198 | player._update_player_state(payload["state"]) 199 | case _: # pyright: ignore - lavalink could add new op codes. 200 | __ws_log__.error( 201 | f"Link '{self.identifier}' received a payload with an unhandled op code: '{payload["op"]}'." 202 | ) 203 | 204 | async def _listen(self) -> None: 205 | while True: 206 | if self._websocket is None: 207 | await self._reset_state() 208 | break 209 | message = await self._websocket.receive() 210 | if message.type is aiohttp.WSMsgType.CLOSED: 211 | # Log a warning for the first reconnect attempt. 212 | if self._backoff.tries == 0: 213 | __ws_log__.warning( 214 | f"Link '{self.identifier}' was unexpectedly disconnected from its websocket. It will now " 215 | f"attempt to reconnect up to {self._backoff.max_tries} times with a backoff delay." 216 | ) 217 | # Calculate the backoff delay and sleep. 218 | __ws_log__.warning( 219 | f"Link '{self.identifier}' is attempting its {ordinal(self._backoff.tries + 1)} reconnection " 220 | f"in {(delay := self._backoff.calculate()):.2f} seconds." 221 | ) 222 | await asyncio.sleep(delay) 223 | 224 | try: 225 | # Attempt to reconnect to the websocket. 226 | await self.connect() 227 | except LinkConnectionError as error: 228 | # Print the error manually because we don't want an error to be raised inside the task. 229 | traceback.print_exception(type(error), error, error.__traceback__) 230 | # Cancel the task (and reset other vars) to stop further reconnection attempts. 231 | if self._backoff.max_tries and self._backoff.tries == self._backoff.max_tries: 232 | __ws_log__.warning( 233 | f"Link '{self.identifier}' has attempted to reconnect {self._backoff.max_tries} times " 234 | f"with no success. It will not attempt to reconnect again." 235 | ) 236 | await self._reset_state() 237 | break 238 | # Continue to the next reconnection attempt. 239 | continue 240 | else: 241 | # If no error was raised, continue the outer loop as we should've reconnected. 242 | __ws_log__.info(f"Link '{self.identifier}' was able to reconnect to its websocket.") 243 | continue 244 | 245 | asyncio.create_task( 246 | self._process_payload( 247 | cast(Payload, self._json_loads(message.data)) 248 | ) 249 | ) 250 | 251 | # rest 252 | 253 | async def _request( 254 | self, 255 | method: RequestMethod, 256 | path: str, 257 | /, *, 258 | parameters: RequestParameters | None = None, 259 | data: RequestData | None = None, 260 | ) -> Any: 261 | if self._session is None: 262 | self._session = aiohttp.ClientSession() 263 | 264 | url = f"{(self._rest_url or f'http://{self._host}:{self._port}/').removesuffix('/')}{path}" 265 | 266 | kwargs: RequestKwargs = {"headers": {"Authorization": self._password}} 267 | if parameters is not None: 268 | kwargs["params"] = parameters 269 | if data is not None: 270 | kwargs["headers"]["Content-Type"] = "application/json" 271 | kwargs["data"] = self._json_dumps(cast(JSON, data)) 272 | 273 | async with self._session.request(method, url, **kwargs) as response: 274 | response_data = await json_or_text(response, json_loads=self._json_loads) 275 | __rest_log__.debug( 276 | f"{method} -> '{url}' -> {response.status}.\n" 277 | f"Request Parameters:{"\n" if parameters else " "}%s\n" 278 | f"Request Data:{"\n" if data else " "}%s\n" 279 | f"Response Data:{"\n" if response_data else " "}%s", 280 | DeferredMessage(_json.dumps, parameters or {}, indent=4), 281 | DeferredMessage(_json.dumps, data or {}, indent=4), 282 | DeferredMessage(_json.dumps, response_data or {}, indent=4), 283 | ) 284 | if 200 <= response.status < 300: 285 | return response_data 286 | 287 | async def _spotify_search(self, _type: SpotifySearchType, _id: str, /) -> Result: 288 | try: 289 | assert self._spotify is not None 290 | match _type: 291 | case "album": 292 | source = await self._spotify.get_full_album(_id) 293 | tracks = [ 294 | Track._from_spotify_track(track) for track in 295 | itertools.chain.from_iterable( 296 | [ 297 | filter( 298 | None, 299 | (await self._spotify.get_tracks([track.id for track in chunk])).values() 300 | ) 301 | for chunk in chunks(source.tracks, 50) 302 | ] 303 | ) 304 | ] 305 | case "playlist": 306 | source = await self._spotify.get_full_playlist(_id) 307 | tracks = [Track._from_spotify_track(track) for track in source.tracks] 308 | case "artist": 309 | source = await self._spotify.get_artist(_id) 310 | tracks = [ 311 | Track._from_spotify_track(track) for track in 312 | await self._spotify.get_artist_top_tracks(_id) 313 | ] 314 | case "track": 315 | source = await self._spotify.get_track(_id) 316 | tracks = [Track._from_spotify_track(source)] 317 | except spotipy.NotFound: 318 | raise NoSearchResults(search=_id) 319 | except spotipy.HTTPError as error: 320 | raise SearchFailed( 321 | { 322 | "message": "Error while accessing the Spotify API.", 323 | "severity": "suspicious", 324 | "cause": error.message 325 | } 326 | ) 327 | # noinspection PyUnboundLocalVariable 328 | return Result(source=source, tracks=tracks) 329 | 330 | async def _lavalink_search(self, search: str, /) -> Result: 331 | data: SearchData = cast( 332 | SearchData, 333 | await self._request("GET", "/v4/loadtracks", parameters={"identifier": search}) 334 | ) 335 | match data["loadType"]: 336 | case "track": 337 | source = Track(data["data"]) 338 | tracks = [source] 339 | case "playlist": 340 | source = Playlist(data["data"]) 341 | tracks = source.tracks 342 | case "search": 343 | source = [Track(track) for track in data["data"]] 344 | tracks = source 345 | case "empty": 346 | raise NoSearchResults(search=search) 347 | case "error": 348 | raise SearchFailed(data["data"]) 349 | case _: # pyright: ignore - lavalink could add new load types 350 | msg = f"Unknown load type: '{data["load_type"]}'. Please report this to the library author." 351 | __rest_log__.error(msg) 352 | raise SearchError(msg) 353 | return Result(source=source, tracks=tracks) 354 | 355 | async def search(self, search: str, /) -> Result: 356 | if (match := SPOTIFY_REGEX.match(search)) is not None and self._spotify is not None: 357 | return await self._spotify_search(match["type"], match["id"]) # pyright: ignore 358 | else: 359 | return await self._lavalink_search(search) 360 | -------------------------------------------------------------------------------- /discord/ext/lava/objects/__init__.py: -------------------------------------------------------------------------------- 1 | from .events import * 2 | from .filters import * 3 | from .playlist import * 4 | from .result import * 5 | from .stats import * 6 | from .track import * 7 | -------------------------------------------------------------------------------- /discord/ext/lava/objects/events.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from ..enums import ExceptionSeverity, TrackEndReason 4 | from ..types.objects.events import EventData, EventType, TrackEndEventData, TrackEventData 5 | from ..types.objects.events import TrackExceptionEventData, TrackStuckEventData, WebSocketClosedEventData 6 | from .track import Track 7 | 8 | 9 | __all__ = [ 10 | "TrackStartEvent", 11 | "TrackEndEvent", 12 | "TrackExceptionEvent", 13 | "TrackStuckEvent", 14 | "WebSocketClosedEvent", 15 | "UnhandledEvent", 16 | ] 17 | 18 | 19 | class _BaseEvent: 20 | __slots__ = ("type", "guild_id", "_dispatch_name",) 21 | 22 | def __init__(self, data: EventData) -> None: 23 | self.type: EventType = data["type"] 24 | self.guild_id: str = data["guildId"] 25 | 26 | 27 | class _BaseTrackEvent(_BaseEvent): 28 | __slots__ = ("track",) 29 | 30 | def __init__(self, data: TrackEventData) -> None: 31 | super().__init__(data) 32 | self.track: Track = Track(data["track"]) 33 | 34 | 35 | class TrackStartEvent(_BaseTrackEvent): 36 | 37 | def __repr__(self) -> str: 38 | return f"" 39 | 40 | 41 | class TrackEndEvent(_BaseTrackEvent): 42 | __slots__ = ("reason",) 43 | 44 | def __init__(self, data: TrackEndEventData) -> None: 45 | super().__init__(data) 46 | self.reason: TrackEndReason = TrackEndReason(data["reason"]) 47 | 48 | def __repr__(self) -> str: 49 | return f"" 50 | 51 | 52 | class TrackExceptionEvent(_BaseTrackEvent): 53 | __slots__ = ("message", "severity", "cause",) 54 | 55 | def __init__(self, data: TrackExceptionEventData) -> None: 56 | super().__init__(data) 57 | exception = data["exception"] 58 | self.message: str | None = exception["message"] 59 | self.severity: ExceptionSeverity = ExceptionSeverity(exception["severity"]) 60 | self.cause: str = exception["cause"] 61 | 62 | def __repr__(self) -> str: 63 | return f"" 65 | 66 | 67 | class TrackStuckEvent(_BaseTrackEvent): 68 | __slots__ = ("threshold_ms",) 69 | 70 | def __init__(self, data: TrackStuckEventData) -> None: 71 | super().__init__(data) 72 | self.threshold_ms: int = data["thresholdMs"] 73 | 74 | def __repr__(self) -> str: 75 | return f"" 76 | 77 | 78 | class WebSocketClosedEvent(_BaseEvent): 79 | __slots__ = ("code", "reason", "by_remote",) 80 | 81 | def __init__(self, data: WebSocketClosedEventData) -> None: 82 | super().__init__(data) 83 | self.code: int = data["code"] 84 | self.reason: str = data["reason"] 85 | self.by_remote: bool = data["byRemote"] 86 | 87 | def __repr__(self) -> str: 88 | return f"" 90 | 91 | 92 | class UnhandledEvent(_BaseEvent): 93 | __slots__ = ("data",) 94 | 95 | def __init__(self, data: EventData) -> None: 96 | super().__init__(data) 97 | self.data: EventData = data 98 | 99 | def __repr__(self) -> str: 100 | return f"" 101 | -------------------------------------------------------------------------------- /discord/ext/lava/objects/filters.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import abc 4 | import collections 5 | from typing import Self 6 | 7 | from ..types.objects.filters import ChannelMixData, DistortionData, EqualizerData, FiltersData, KaraokeData 8 | from ..types.objects.filters import LowPassData, RotationData, TimescaleData, TremoloData, VibratoData 9 | 10 | 11 | __all__ = [ 12 | "Equalizer", 13 | "Karaoke", 14 | "Timescale", 15 | "Tremolo", 16 | "Vibrato", 17 | "Rotation", 18 | "Distortion", 19 | "ChannelMix", 20 | "LowPass", 21 | "Filter", 22 | ] 23 | 24 | 25 | class _FilterBase(metaclass=abc.ABCMeta): 26 | __slots__ = () 27 | 28 | def __repr__(self) -> str: 29 | attributes = [f"{x.lstrip("_")}={getattr(self, x)}" for x in self.__slots__] 30 | return f"" 31 | 32 | @property 33 | @abc.abstractmethod 34 | def data(self) -> ...: 35 | raise NotImplementedError 36 | 37 | 38 | class Equalizer(_FilterBase): 39 | __slots__ = ("_bands",) 40 | 41 | def __init__(self, *, bands: list[tuple[int, float]] | None = None) -> None: 42 | self._bands: collections.defaultdict[int, float] = collections.defaultdict(float) 43 | 44 | if bands: 45 | if any((band, gain) for band, gain in bands if band < 0 or band > 14 or gain < -0.25 or gain > 1.0): 46 | raise ValueError("Equalizer bands must be between 0 and 14 and gains must be between -0.25 and 1.0.") 47 | self._bands.update(bands) 48 | 49 | @property 50 | def data(self) -> EqualizerData: 51 | return [{"band": band, "gain": self._bands[band]} for band in range(15)] 52 | 53 | 54 | class Karaoke(_FilterBase): 55 | __slots__ = ("_level", "_mono_level", "_filter_band", "_filter_width",) 56 | 57 | def __init__( 58 | self, 59 | *, 60 | level: float = 1.0, 61 | mono_level: float = 1.0, 62 | filter_band: float = 220.0, 63 | filter_width: float = 100.0 64 | ) -> None: 65 | self._level: float = level 66 | self._mono_level: float = mono_level 67 | self._filter_band: float = filter_band 68 | self._filter_width: float = filter_width 69 | 70 | @property 71 | def data(self) -> KaraokeData: 72 | return { 73 | "level": self._level, 74 | "monoLevel": self._mono_level, 75 | "filterBand": self._filter_band, 76 | "filterWidth": self._filter_width, 77 | } 78 | 79 | 80 | class Timescale(_FilterBase): 81 | __slots__ = ("_pitch", "_speed", "_rate",) 82 | 83 | def __init__( 84 | self, 85 | *, 86 | pitch: float = 1.0, 87 | speed: float = 1.0, 88 | rate: float = 1.0, 89 | ) -> None: 90 | if pitch < 0.0: 91 | raise ValueError("'pitch' must be more than or equal to 0.0.") 92 | if speed < 0.0: 93 | raise ValueError("'speed' must be more than or equal to 0.0.") 94 | if rate < 0.0: 95 | raise ValueError("'rate' must be more than or equal to 0.0.") 96 | 97 | self._pitch: float = pitch 98 | self._speed: float = speed 99 | self._rate: float = rate 100 | 101 | @property 102 | def data(self) -> TimescaleData: 103 | return { 104 | "pitch": self._pitch, 105 | "speed": self._speed, 106 | "rate": self._rate 107 | } 108 | 109 | 110 | class Tremolo(_FilterBase): 111 | __slots__ = ("_frequency", "_depth",) 112 | 113 | def __init__( 114 | self, 115 | *, 116 | frequency: float = 2.0, 117 | depth: float = 0.0 118 | ) -> None: 119 | if frequency <= 0.0: 120 | raise ValueError("'frequency' must be more than 0.0.") 121 | if depth <= 0.0 or depth > 1.0: 122 | raise ValueError("'depth' must be more than 0.0 and less than or equal to 1.0.") 123 | 124 | self._frequency: float = frequency 125 | self._depth: float = depth 126 | 127 | @property 128 | def data(self) -> TremoloData: 129 | return { 130 | "frequency": self._frequency, 131 | "depth": self._depth, 132 | } 133 | 134 | 135 | class Vibrato(_FilterBase): 136 | __slots__ = ("_frequency", "_depth",) 137 | 138 | def __init__( 139 | self, 140 | *, 141 | frequency: float = 2.0, 142 | depth: float = 0.0 143 | ) -> None: 144 | if frequency <= 0.0 or frequency > 14.0: 145 | raise ValueError("'frequency' must be more than 0.0 and less than or equal to 14.0.") 146 | if depth <= 0.0 or depth > 1.0: 147 | raise ValueError("'depth' must be more than 0.0 and less than or equal to 1.0.") 148 | 149 | self._frequency: float = frequency 150 | self._depth: float = depth 151 | 152 | @property 153 | def data(self) -> VibratoData: 154 | return { 155 | "frequency": self._frequency, 156 | "depth": self._depth, 157 | } 158 | 159 | 160 | class Rotation(_FilterBase): 161 | __slots__ = ("_speed",) 162 | 163 | def __init__(self, *, speed: float = 0.0) -> None: 164 | self._speed: float = speed 165 | 166 | @property 167 | def data(self) -> RotationData: 168 | return {"rotationHz": self._speed} 169 | 170 | 171 | class Distortion(_FilterBase): 172 | __slots__ = ( 173 | "_sin_offset", "_sin_scale", "_cos_offset", "_cos_scale", "_tan_offset", "_tan_scale", "_offset", "_scale", 174 | ) 175 | 176 | def __init__( 177 | self, 178 | *, 179 | sin_offset: float = 0.0, 180 | sin_scale: float = 1.0, 181 | cos_offset: float = 0.0, 182 | cos_scale: float = 1.0, 183 | tan_offset: float = 0.0, 184 | tan_scale: float = 1.0, 185 | offset: float = 0.0, 186 | scale: float = 1.0 187 | ) -> None: 188 | self._sin_offset: float = sin_offset 189 | self._sin_scale: float = sin_scale 190 | self._cos_offset: float = cos_offset 191 | self._cos_scale: float = cos_scale 192 | self._tan_offset: float = tan_offset 193 | self._tan_scale: float = tan_scale 194 | self._offset: float = offset 195 | self._scale: float = scale 196 | 197 | @property 198 | def data(self) -> DistortionData: 199 | return { 200 | "sinOffset": self._sin_offset, 201 | "sinScale": self._sin_scale, 202 | "cosOffset": self._cos_offset, 203 | "cosScale": self._cos_scale, 204 | "tanOffset": self._tan_offset, 205 | "tanScale": self._tan_scale, 206 | "offset": self._offset, 207 | "scale": self._scale, 208 | } 209 | 210 | 211 | class ChannelMix(_FilterBase): 212 | __slots__ = ("_left_to_left", "_left_to_right", "_right_to_left", "_right_to_right",) 213 | 214 | def __init__( 215 | self, 216 | *, 217 | left_to_left: float = 1.0, 218 | left_to_right: float = 0.0, 219 | right_to_left: float = 0.0, 220 | right_to_right: float = 1.0, 221 | ) -> None: 222 | if any( 223 | value for value in (left_to_left, left_to_right, right_to_left, right_to_right) 224 | if value < 0.0 or value > 1.0 225 | ): 226 | raise ValueError( 227 | "'left_to_left', 'left_to_right', 'right_to_left', and 'right_to_right' " 228 | "must all be more than or equal to 0.0 and less than or equal to 1.0" 229 | ) 230 | 231 | self._left_to_left: float = left_to_left 232 | self._left_to_right: float = left_to_right 233 | self._right_to_left: float = right_to_left 234 | self._right_to_right: float = right_to_right 235 | 236 | @property 237 | def data(self) -> ChannelMixData: 238 | return { 239 | "leftToLeft": self._left_to_left, 240 | "leftToRight": self._left_to_right, 241 | "rightToLeft": self._right_to_left, 242 | "rightToRight": self._right_to_right 243 | } 244 | 245 | @classmethod 246 | def mono(cls) -> Self: 247 | return cls(left_to_left=0.5, left_to_right=0.5, right_to_left=0.5, right_to_right=0.5) 248 | 249 | @classmethod 250 | def switch(cls) -> Self: 251 | return cls(left_to_left=0.0, left_to_right=1.0, right_to_left=1.0, right_to_right=0.0) 252 | 253 | @classmethod 254 | def only_left(cls) -> Self: 255 | return cls(left_to_left=1.0, left_to_right=0.0, right_to_left=0.0, right_to_right=0.0) 256 | 257 | @classmethod 258 | def full_left(cls) -> Self: 259 | return cls(left_to_left=0.5, left_to_right=0.0, right_to_left=0.5, right_to_right=0.0) 260 | 261 | @classmethod 262 | def only_right(cls) -> Self: 263 | return cls(left_to_left=0.0, left_to_right=0.0, right_to_left=0.0, right_to_right=1.0) 264 | 265 | @classmethod 266 | def full_right(cls) -> Self: 267 | return cls(left_to_left=0.0, left_to_right=0.5, right_to_left=0.0, right_to_right=0.5) 268 | 269 | 270 | class LowPass(_FilterBase): 271 | __slots__ = ("_smoothing",) 272 | 273 | def __init__(self, *, smoothing: float = 1.0) -> None: 274 | if smoothing < 1.0: 275 | raise ValueError("'smoothing' must be more than or equal to 1.0.") 276 | self._smoothing: float = smoothing 277 | 278 | @property 279 | def data(self) -> LowPassData: 280 | return {"smoothing": self._smoothing} 281 | 282 | 283 | class Filter(_FilterBase): 284 | __slots__ = ( 285 | "_filter", "_equalizer", "_karaoke", "_timescale", "_tremolo", "_vibrato", "_rotation", "_distortion", 286 | "_channel_mix", "_low_pass", 287 | ) 288 | 289 | def __init__( 290 | self, 291 | filter: Filter | None = None, 292 | equalizer: Equalizer | None = None, 293 | karaoke: Karaoke | None = None, 294 | timescale: Timescale | None = None, 295 | tremolo: Tremolo | None = None, 296 | vibrato: Vibrato | None = None, 297 | rotation: Rotation | None = None, 298 | distortion: Distortion | None = None, 299 | channel_mix: ChannelMix | None = None, 300 | low_pass: LowPass | None = None, 301 | ) -> None: 302 | self._filter: Filter | None = filter 303 | self._equalizer: Equalizer | None = equalizer 304 | self._karaoke: Karaoke | None = karaoke 305 | self._timescale: Timescale | None = timescale 306 | self._tremolo: Tremolo | None = tremolo 307 | self._vibrato: Vibrato | None = vibrato 308 | self._rotation: Rotation | None = rotation 309 | self._distortion: Distortion | None = distortion 310 | self._channel_mix: ChannelMix | None = channel_mix 311 | self._low_pass: LowPass | None = low_pass 312 | 313 | @property 314 | def data(self) -> FiltersData: 315 | payload: FiltersData = self._filter.data if self._filter else {} 316 | 317 | if self._equalizer: 318 | payload["equalizer"] = self._equalizer.data 319 | if self._karaoke: 320 | payload["karaoke"] = self._karaoke.data 321 | if self._timescale: 322 | payload["timescale"] = self._timescale.data 323 | if self._tremolo: 324 | payload["tremolo"] = self._tremolo.data 325 | if self._vibrato: 326 | payload["vibrato"] = self._vibrato.data 327 | if self._rotation: 328 | payload["rotation"] = self._rotation.data 329 | if self._distortion: 330 | payload["distortion"] = self._distortion.data 331 | if self._channel_mix: 332 | payload["channelMix"] = self._channel_mix.data 333 | if self._low_pass: 334 | payload["lowPass"] = self._low_pass.data 335 | 336 | return payload 337 | -------------------------------------------------------------------------------- /discord/ext/lava/objects/playlist.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING 4 | 5 | from .track import Track 6 | 7 | 8 | if TYPE_CHECKING: 9 | from ..types.objects.playlist import PlaylistData 10 | 11 | 12 | __all__ = ["Playlist"] 13 | 14 | 15 | class Playlist: 16 | __slots__ = ("name", "selected_track", "tracks",) 17 | 18 | def __init__(self, data: PlaylistData) -> None: 19 | info = data["info"] 20 | self.name: str = info["name"] 21 | self.selected_track: int = info["selectedTrack"] 22 | self.tracks: list[Track] = [Track(track) for track in data["tracks"]] 23 | 24 | def __repr__(self) -> str: 25 | return f"" 26 | -------------------------------------------------------------------------------- /discord/ext/lava/objects/result.py: -------------------------------------------------------------------------------- 1 | from collections.abc import Sequence 2 | 3 | from ..types.common import SpotifySource 4 | from .playlist import Playlist 5 | from .track import Track 6 | 7 | 8 | __all__ = ["Result"] 9 | 10 | 11 | class Result: 12 | 13 | def __init__( 14 | self, 15 | *, 16 | source: SpotifySource | Playlist | Sequence[Track] | Track, 17 | tracks: Sequence[Track], 18 | ) -> None: 19 | self.source: SpotifySource | Playlist | Sequence[Track] | Track = source 20 | self.tracks: Sequence[Track] = tracks 21 | 22 | def __repr__(self) -> str: 23 | return f"" 24 | -------------------------------------------------------------------------------- /discord/ext/lava/objects/stats.py: -------------------------------------------------------------------------------- 1 | from ..types.objects.stats import StatsData 2 | 3 | 4 | __all__ = ["Stats"] 5 | 6 | 7 | class Stats: 8 | __slots__ = ( 9 | "players", "playing_players", "uptime", "memory_free", "memory_used", "memory_allocated", 10 | "memory_reservable", "cpu_cores", "system_load", "lavalink_load", "frames_sent", "frames_nulled", 11 | "frames_deficit", 12 | ) 13 | 14 | def __init__(self, data: StatsData) -> None: 15 | # general 16 | self.players: int = data["players"] 17 | self.playing_players: int = data["playingPlayers"] 18 | self.uptime: int = data["uptime"] 19 | # memory 20 | memory = data["memory"] 21 | self.memory_free: int = memory["free"] 22 | self.memory_used: int = memory["used"] 23 | self.memory_allocated: int = memory["allocated"] 24 | self.memory_reservable: int = memory["reservable"] 25 | # cpu 26 | cpu = data["cpu"] 27 | self.cpu_cores: int = cpu["cores"] 28 | self.system_load: float = cpu["systemLoad"] 29 | self.lavalink_load: float = cpu["lavalinkLoad"] 30 | # frame stats 31 | frame_stats = data["frameStats"] or {} 32 | self.frames_sent: int = frame_stats.get("sent", -1) 33 | self.frames_nulled: int = frame_stats.get("nulled", -1) 34 | self.frames_deficit: int = frame_stats.get("deficit", -1) 35 | 36 | def __repr__(self) -> str: 37 | return f"" 38 | -------------------------------------------------------------------------------- /discord/ext/lava/objects/track.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import spotipy 4 | 5 | from .._utilities import MISSING 6 | from ..types.objects.track import TrackData, TrackInfoData, TrackPluginInfoData, TrackUserData 7 | 8 | 9 | __all__ = ["Track"] 10 | 11 | 12 | class Track: 13 | __slots__ = ( 14 | "encoded", "identifier", "_is_seekable", "author", "length", "_is_stream", "position", "title", "uri", 15 | "artwork_url", "isrc", "source", "plugin_info", "user_data", 16 | ) 17 | 18 | def __init__(self, data: TrackData) -> None: 19 | # encoded track 20 | self.encoded: str = data["encoded"] 21 | # track info 22 | info: TrackInfoData = data["info"] 23 | self.identifier: str = info["identifier"] 24 | self._is_seekable: bool = info["isSeekable"] 25 | self.author: str = info["author"] 26 | self.length: int = info["length"] 27 | self._is_stream: bool = info["isStream"] 28 | self.position: int = info["position"] 29 | self.title: str = info["title"] 30 | self.uri: str | None = info["uri"] 31 | self.artwork_url: str | None = info["artworkUrl"] 32 | self.isrc: str | None = info["isrc"] 33 | self.source: str = info["sourceName"] 34 | # others 35 | self.plugin_info: TrackPluginInfoData = data["pluginInfo"] 36 | self.user_data: TrackUserData = data["userData"] 37 | 38 | def __repr__(self) -> str: 39 | return f"" 41 | 42 | # utility methods 43 | 44 | def is_seekable(self) -> bool: 45 | return self._is_seekable 46 | 47 | def is_stream(self) -> bool: 48 | return self._is_stream 49 | 50 | # spotify 51 | 52 | @classmethod 53 | def _from_spotify_track(cls, track: spotipy.Track | spotipy.PlaylistTrack) -> Track: 54 | if track.is_local: 55 | identifier = track.uri 56 | author = track.artists[0].name or "Unknown" 57 | title = track.name or "Unknown" 58 | artwork_url = None 59 | isrc = None 60 | else: 61 | identifier = track.id 62 | author = ", ".join(artist.name for artist in track.artists) 63 | title = track.name 64 | artwork_url = track.album.images[0].url if len(track.album.images) > 0 else None 65 | isrc = track.external_ids.get("isrc") 66 | return Track( 67 | { 68 | "encoded": MISSING, 69 | "info": { 70 | "identifier": identifier, 71 | "isSeekable": True, 72 | "author": author, 73 | "length": track.duration_ms, 74 | "isStream": False, 75 | "position": 0, 76 | "title": title, 77 | "uri": track.uri, 78 | "artworkUrl": artwork_url, 79 | "isrc": isrc, 80 | "sourceName": "spotify", 81 | }, 82 | "pluginInfo": {}, 83 | "userData": {}, 84 | } 85 | ) 86 | -------------------------------------------------------------------------------- /discord/ext/lava/player.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import json 4 | import logging 5 | import time 6 | from typing import Generic, cast 7 | from typing_extensions import TypeVar 8 | 9 | import discord 10 | import discord.types.voice 11 | 12 | from ._utilities import MISSING, DeferredMessage, get_event_dispatch_name 13 | from .exceptions import PlayerAlreadyConnected, PlayerNotConnected 14 | from .link import Link 15 | from .objects.events import TrackEndEvent, TrackExceptionEvent, TrackStartEvent, TrackStuckEvent 16 | from .objects.events import UnhandledEvent, WebSocketClosedEvent 17 | from .objects.filters import Filter 18 | from .objects.track import Track 19 | from .types.common import PlayerStateData, VoiceChannel 20 | from .types.objects.track import TrackUserData 21 | from .types.rest import PlayerData, UpdatePlayerRequestData, UpdatePlayerRequestParameters 22 | from .types.rest import UpdatePlayerRequestTrackData 23 | from .types.websocket import EventPayload 24 | 25 | 26 | __all__ = ["Player"] 27 | __log__: logging.Logger = logging.getLogger("lava.player") 28 | 29 | BotT = TypeVar("BotT", bound=discord.Client, default=discord.Client, covariant=True) 30 | 31 | type Connectable = discord.abc.Connectable 32 | type VoiceStateUpdateData = discord.types.voice.GuildVoiceState 33 | type VoiceServerUpdateData = discord.types.voice.VoiceServerUpdate 34 | 35 | 36 | class Player(discord.VoiceProtocol, Generic[BotT]): 37 | 38 | def __init__(self, link: Link) -> None: 39 | self._bot: BotT = MISSING 40 | self._guild: discord.Guild = MISSING 41 | self._channel: VoiceChannel | None = MISSING 42 | self._link: Link = link 43 | # player voice state 44 | self._token: str | None = None 45 | self._endpoint: str | None = None 46 | self._session_id: str | None = None 47 | # player state 48 | self._connected: bool = False 49 | self._ping: int = -1 50 | self._position: int = 0 51 | self._time: int = 0 52 | # player data 53 | self._track: Track | None = None 54 | self._filter: Filter | None = None 55 | self._paused = False 56 | self._volume = 100 57 | 58 | def __call__(self, client: discord.Client, channel: Connectable) -> Player: 59 | self._bot = client # type: ignore 60 | self._guild = channel.guild # type: ignore 61 | self._channel = channel # type: ignore 62 | self._link._players[self.guild.id] = self # type: ignore 63 | return self 64 | 65 | def __repr__(self) -> str: 66 | return f"" 67 | 68 | # properties 69 | 70 | @property 71 | def guild(self) -> discord.Guild: 72 | return self._guild 73 | 74 | @property 75 | def channel(self) -> VoiceChannel | None: # pyright: ignore 76 | return self._channel 77 | 78 | @property 79 | def ping(self) -> int: 80 | return self._ping 81 | 82 | @property 83 | def time(self) -> int: 84 | return self._time 85 | 86 | # voice state update 87 | 88 | async def on_voice_state_update(self, data: VoiceStateUpdateData, /) -> None: 89 | __log__.debug( 90 | f"Player ({self.guild.id} : {self.guild.name}) received a 'VOICE_STATE_UPDATE' from Discord.\n" 91 | f"%s", DeferredMessage(json.dumps, data, indent=4), 92 | ) 93 | # set discord voice state data 94 | self._session_id = data["session_id"] 95 | await self._update_voice_state() 96 | # update player voice channel 97 | id: int | None = int(channel_id) if (channel_id := data["channel_id"]) else None # type: ignore 98 | channel: VoiceChannel | None = self.guild.get_channel(id) # type: ignore 99 | if self._channel == channel: 100 | return 101 | elif self._channel is not None and channel is None: 102 | __log__.info( 103 | f"Player ({self.guild.id} : {self.guild.name}) disconnected from voice channel " 104 | f"({self._channel.id} : {self._channel.name})." 105 | ) 106 | elif self._channel is None and channel is not None: 107 | __log__.info( 108 | f"Player ({self.guild.id} : {self.guild.name}) connected to voice channel " 109 | f"({channel.id} : {channel.name})." 110 | ) 111 | elif self._channel is not None and channel is not None: 112 | __log__.info( 113 | f"Player ({self.guild.id} : {self.guild.name}) moved from voice channel " 114 | f"({self._channel.id} : {self._channel.name}) to ({channel.id} : {channel.name})." 115 | ) 116 | self._channel = channel 117 | 118 | async def on_voice_server_update(self, data: VoiceServerUpdateData, /) -> None: 119 | __log__.debug( 120 | f"Player ({self.guild.id} : {self.guild.name}) received a 'VOICE_SERVER_UPDATE' from Discord.\n" 121 | f"%s", DeferredMessage(json.dumps, data, indent=4), 122 | ) 123 | # set discord voice state data 124 | self._token = data["token"] 125 | self._endpoint = data["endpoint"] 126 | await self._update_voice_state() 127 | 128 | async def _update_voice_state(self) -> None: 129 | # check if all the data required to update the player's voice state is available 130 | if self._token is None or self._endpoint is None or self._session_id is None: 131 | return 132 | # update the player's voice state 133 | await self._link._request( 134 | "PATCH", f"/v4/sessions/{self._link.session_id}/players/{self.guild.id}", 135 | data={"voice": {"token": self._token, "endpoint": self._endpoint, "sessionId": self._session_id}}, 136 | ) 137 | # reset the discord voice state data to disallow subsequent 'VOICE_STATE_UPDATE' 138 | # events from updating the player's voice state unnecessarily 139 | self._token, self._endpoint, self._session_id = None, None, None 140 | 141 | # events + player updates 142 | 143 | def _dispatch_event(self, payload: EventPayload, /) -> None: 144 | match payload["type"]: 145 | case "TrackStartEvent": 146 | event = TrackStartEvent(payload) 147 | case "TrackEndEvent": 148 | event = TrackEndEvent(payload) 149 | case "TrackExceptionEvent": 150 | event = TrackExceptionEvent(payload) 151 | case "TrackStuckEvent": 152 | event = TrackStuckEvent(payload) 153 | case "WebSocketClosedEvent": 154 | event = WebSocketClosedEvent(payload) 155 | case _: # pyright: ignore - lavalink could add new event types 156 | event = UnhandledEvent(payload) 157 | self._bot.dispatch(get_event_dispatch_name(event.type), self, event) 158 | __log__.info(f"Player ({self.guild.id} : {self.guild.name}) dispatched '{event}'") 159 | 160 | def _update_player_state(self, payload: PlayerStateData, /) -> None: 161 | self._connected = payload["connected"] 162 | self._ping = payload["ping"] 163 | self._position = payload["position"] 164 | self._time = payload["time"] 165 | 166 | def _update_player_data(self, data: PlayerData) -> None: 167 | self._update_player_state(data["state"]) 168 | self._track = Track(data["track"]) if data["track"] else None 169 | # self._filter = Filter(data["filters"]) 170 | self._volume = data["volume"] 171 | self._paused = data["paused"] 172 | 173 | # methods 174 | 175 | async def update( 176 | self, 177 | *, 178 | track: Track | None = MISSING, 179 | track_identifier: str = MISSING, 180 | track_user_data: TrackUserData = MISSING, 181 | track_end_time: int | None = MISSING, 182 | replace_current_track: bool = MISSING, 183 | filter: Filter = MISSING, 184 | position: int = MISSING, 185 | paused: bool = MISSING, 186 | volume: int = MISSING, 187 | ) -> None: 188 | # validate that track and track_identifier are not used at the same time 189 | if track is not MISSING and track_identifier is not MISSING: 190 | raise ValueError("'track' and 'track_identifier' can not be used at the same time.") 191 | # wait for the player to be ready 192 | if not self._link.is_ready(): 193 | await self._link._ready_event.wait() 194 | 195 | # prepare request parameters 196 | parameters: UpdatePlayerRequestParameters = {} 197 | if replace_current_track is not MISSING: 198 | parameters["noReplace"] = not replace_current_track 199 | 200 | # prepare request track data 201 | track_data: UpdatePlayerRequestTrackData = {} 202 | if track is not MISSING: 203 | track_data["encoded"] = track.encoded if track else None 204 | if track_identifier is not MISSING: 205 | track_data["identifier"] = track_identifier 206 | if track_user_data is not MISSING: 207 | track_data["userData"] = track_user_data 208 | 209 | # prepare request data 210 | data: UpdatePlayerRequestData = {} 211 | if track_data: 212 | data["track"] = track_data 213 | if track_end_time is not MISSING: 214 | if isinstance(track_end_time, int) and track_end_time <= 0: 215 | raise TypeError("'track_end_time' must be an integer more than or equal to '1'.") 216 | data["endTime"] = track_end_time 217 | if filter is not MISSING: 218 | data["filters"] = filter.data 219 | if position is not MISSING: 220 | data["position"] = position 221 | if paused is not MISSING: 222 | data["paused"] = paused 223 | if volume is not MISSING: 224 | if volume < 0 or volume > 1000: 225 | raise TypeError("'volume' must be an integer between '0' and '1000' inclusive.") 226 | data["volume"] = volume 227 | 228 | # send request 229 | player: PlayerData = await self._link._request( 230 | "PATCH", f"/v4/sessions/{self._link.session_id}/players/{self.guild.id}", 231 | parameters=parameters, data=data, 232 | ) 233 | self._update_player_data(player) 234 | 235 | # connection 236 | 237 | def is_connected(self) -> bool: 238 | return self._channel is not None and self._connected is True 239 | 240 | async def connect( 241 | self, *, 242 | channel: VoiceChannel | None = None, 243 | timeout: float | None = None, 244 | reconnect: bool | None = None, 245 | self_deaf: bool | None = None, 246 | self_mute: bool | None = None, 247 | ) -> None: 248 | if self.is_connected(): 249 | raise PlayerAlreadyConnected("This player is already connected to a voice channel.") 250 | if self._channel is None and channel is None: 251 | raise ValueError("You must provide the 'channel' parameter to reconnect this player.") 252 | self._channel = cast(VoiceChannel, self._channel or channel) 253 | __log__.info( 254 | f"Player ({self.guild.id} : {self.guild.name}) connected to voice channel " 255 | f"({self._channel.id} : {self._channel.name})." 256 | ) 257 | await self.guild.change_voice_state(channel=self._channel) 258 | 259 | async def move_to(self, channel: VoiceChannel) -> None: 260 | if not self.is_connected(): 261 | raise PlayerNotConnected("This player is not connected to a voice channel.") 262 | self._channel = channel 263 | __log__.info( 264 | f"Player ({self.guild.id} : {self.guild.name}) moved from voice channel " 265 | f"({self._channel.id} : {self._channel.name}) to ({channel.id} : {channel.name})." 266 | ) 267 | await self.guild.change_voice_state(channel=self._channel) 268 | 269 | async def disconnect(self, *, force: bool = False) -> None: 270 | if not self.is_connected(): 271 | raise PlayerNotConnected("This player is not connected to a voice channel.") 272 | old_channel = cast(VoiceChannel, self._channel) 273 | self._channel = None 274 | __log__.info( 275 | f"Player ({self.guild.id} : {self.guild.name}) disconnected from voice channel " 276 | f"({old_channel.id} : {old_channel.name})." 277 | ) 278 | await self.guild.change_voice_state(channel=self._channel) 279 | 280 | # position 281 | 282 | @property 283 | def position(self) -> int: 284 | return round(self._position + ((time.time() * 1000) - self._time)) 285 | 286 | async def set_position(self, position: int, /) -> None: 287 | await self.update(position=position) 288 | __log__.info(f"Player ({self.guild.id} : {self.guild.name}) set it's position to '{self.position}'.") 289 | 290 | # track 291 | 292 | @property 293 | def track(self) -> Track | None: 294 | return self._track 295 | 296 | async def play( 297 | self, 298 | track: Track, 299 | /, *, 300 | end_time: int | None = None, 301 | replace_current_track: bool = True, 302 | ) -> None: 303 | await self.update(track=track, track_end_time=end_time, replace_current_track=replace_current_track) 304 | __log__.info( 305 | f"Player ({self.guild.id} : {self.guild.name}) started playing '{track.title}' by '{track.author}'." 306 | ) 307 | 308 | async def stop(self) -> None: 309 | await self.update(track=None) 310 | __log__.info(f"Player ({self.guild.id} : {self.guild.name}) stopped playing.") 311 | 312 | # filter 313 | 314 | @property 315 | def filter(self) -> Filter | None: 316 | return self._filter 317 | 318 | async def set_filter( 319 | self, 320 | filter: Filter, 321 | /, *, 322 | instant: bool = True 323 | ) -> None: 324 | await self.update(filter=filter, position=self.position if instant else MISSING) 325 | __log__.info(f"Player ({self.guild.id} : {self.guild.name}) set it's filter to '{filter}'.") 326 | 327 | # paused 328 | 329 | def is_paused(self) -> bool: 330 | return self._paused 331 | 332 | async def pause(self) -> None: 333 | await self.set_pause_state(True) 334 | 335 | async def set_pause_state(self, state: bool, /) -> None: 336 | await self.update(paused=state) 337 | __log__.info(f"Player ({self.guild.id} : {self.guild.name}) set it's paused state to '{state}'.") 338 | 339 | async def resume(self) -> None: 340 | await self.set_pause_state(False) 341 | 342 | # volume 343 | 344 | @property 345 | def volume(self) -> int: 346 | return self._volume 347 | 348 | async def set_volume(self, volume: int, /) -> None: 349 | await self.update(volume=volume) 350 | -------------------------------------------------------------------------------- /discord/ext/lava/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronhnsy/discord-ext-lava/7f912dac331003c1cac174573b7d7ecf0183345b/discord/ext/lava/py.typed -------------------------------------------------------------------------------- /discord/ext/lava/types/common.py: -------------------------------------------------------------------------------- 1 | from collections.abc import Callable 2 | from typing import Literal, TypedDict 3 | 4 | import discord 5 | import spotipy 6 | 7 | 8 | type JSON = dict[str, "JSON"] | list["JSON"] | str | int | float | bool | None 9 | type JSONDumps = Callable[[JSON], str] 10 | type JSONLoads = Callable[..., JSON] 11 | 12 | type VoiceChannel = discord.channel.VocalGuildChannel 13 | 14 | type SpotifySearchType = Literal["album", "playlist", "artist", "track"] 15 | type SpotifySource = spotipy.Album | spotipy.Playlist | spotipy.Artist | spotipy.Track 16 | 17 | 18 | class PlayerStateData(TypedDict): 19 | connected: bool 20 | ping: int 21 | time: int 22 | position: int 23 | 24 | 25 | class ExceptionData(TypedDict): 26 | message: str | None 27 | severity: Literal["common", "suspicious", "fatal"] 28 | cause: str 29 | -------------------------------------------------------------------------------- /discord/ext/lava/types/objects/events.py: -------------------------------------------------------------------------------- 1 | from typing import Literal, TypedDict 2 | 3 | from ..common import ExceptionData 4 | from .track import TrackData 5 | 6 | 7 | class TrackStartEventData(TypedDict): 8 | op: Literal["event"] 9 | type: Literal["TrackStartEvent"] 10 | guildId: str 11 | track: TrackData 12 | 13 | 14 | class TrackEndEventData(TypedDict): 15 | op: Literal["event"] 16 | type: Literal["TrackEndEvent"] 17 | guildId: str 18 | track: TrackData 19 | reason: Literal["finished", "loadFailed", "stopped", "replaced", "cleanup"] 20 | 21 | 22 | class TrackExceptionEventData(TypedDict): 23 | op: Literal["event"] 24 | type: Literal["TrackExceptionEvent"] 25 | guildId: str 26 | track: TrackData 27 | exception: ExceptionData 28 | 29 | 30 | class TrackStuckEventData(TypedDict): 31 | op: Literal["event"] 32 | type: Literal["TrackStuckEvent"] 33 | guildId: str 34 | track: TrackData 35 | thresholdMs: int 36 | 37 | 38 | class WebSocketClosedEventData(TypedDict): 39 | op: Literal["event"] 40 | type: Literal["WebSocketClosedEvent"] 41 | guildId: str 42 | code: int 43 | reason: str 44 | byRemote: bool 45 | 46 | 47 | type EventType = Literal[ 48 | "TrackStartEvent", 49 | "TrackEndEvent", 50 | "TrackExceptionEvent", 51 | "TrackStuckEvent", 52 | "WebSocketClosedEvent" 53 | ] 54 | type TrackEventData = TrackStartEventData | TrackEndEventData | TrackExceptionEventData | TrackStuckEventData 55 | type EventData = TrackEventData | WebSocketClosedEventData 56 | -------------------------------------------------------------------------------- /discord/ext/lava/types/objects/filters.py: -------------------------------------------------------------------------------- 1 | from typing import NotRequired, TypedDict 2 | 3 | 4 | class EqualizerBandData(TypedDict): 5 | band: int 6 | gain: float 7 | 8 | 9 | EqualizerData = list[EqualizerBandData] 10 | 11 | 12 | class KaraokeData(TypedDict): 13 | level: NotRequired[float] 14 | monoLevel: NotRequired[float] 15 | filterBand: NotRequired[float] 16 | filterWidth: NotRequired[float] 17 | 18 | 19 | class TimescaleData(TypedDict): 20 | pitch: NotRequired[float] 21 | speed: NotRequired[float] 22 | rate: NotRequired[float] 23 | 24 | 25 | class TremoloData(TypedDict): 26 | frequency: NotRequired[float] 27 | depth: NotRequired[float] 28 | 29 | 30 | class VibratoData(TypedDict): 31 | frequency: NotRequired[float] 32 | depth: NotRequired[float] 33 | 34 | 35 | class RotationData(TypedDict): 36 | rotationHz: NotRequired[float] 37 | 38 | 39 | class DistortionData(TypedDict): 40 | sinOffset: NotRequired[float] 41 | sinScale: NotRequired[float] 42 | cosOffset: NotRequired[float] 43 | cosScale: NotRequired[float] 44 | tanOffset: NotRequired[float] 45 | tanScale: NotRequired[float] 46 | offset: NotRequired[float] 47 | scale: NotRequired[float] 48 | 49 | 50 | class ChannelMixData(TypedDict): 51 | leftToLeft: NotRequired[float] 52 | leftToRight: NotRequired[float] 53 | rightToLeft: NotRequired[float] 54 | rightToRight: NotRequired[float] 55 | 56 | 57 | class LowPassData(TypedDict): 58 | smoothing: NotRequired[float] 59 | 60 | 61 | class FiltersData(TypedDict): 62 | equalizer: NotRequired[EqualizerData] 63 | karaoke: NotRequired[KaraokeData] 64 | timescale: NotRequired[TimescaleData] 65 | tremolo: NotRequired[TremoloData] 66 | vibrato: NotRequired[VibratoData] 67 | rotation: NotRequired[RotationData] 68 | distortion: NotRequired[DistortionData] 69 | channelMix: NotRequired[ChannelMixData] 70 | lowPass: NotRequired[LowPassData] 71 | -------------------------------------------------------------------------------- /discord/ext/lava/types/objects/playlist.py: -------------------------------------------------------------------------------- 1 | from typing import Any, TypedDict 2 | 3 | from .track import TrackData 4 | 5 | 6 | class PlaylistInfoData(TypedDict): 7 | name: str 8 | selectedTrack: int 9 | 10 | 11 | type PlaylistPluginInfoData = dict[str, Any] 12 | 13 | 14 | class PlaylistData(TypedDict): 15 | info: PlaylistInfoData 16 | pluginInfo: PlaylistPluginInfoData 17 | tracks: list[TrackData] 18 | -------------------------------------------------------------------------------- /discord/ext/lava/types/objects/stats.py: -------------------------------------------------------------------------------- 1 | from typing import TypedDict 2 | 3 | 4 | class MemoryStatsData(TypedDict): 5 | free: int 6 | used: int 7 | allocated: int 8 | reservable: int 9 | 10 | 11 | class CPUStatsData(TypedDict): 12 | cores: int 13 | systemLoad: float 14 | lavalinkLoad: float 15 | 16 | 17 | class FrameStatsData(TypedDict): 18 | sent: int 19 | nulled: int 20 | deficit: int 21 | 22 | 23 | class StatsData(TypedDict): 24 | players: int 25 | playingPlayers: int 26 | uptime: int 27 | memory: MemoryStatsData 28 | cpu: CPUStatsData 29 | frameStats: FrameStatsData | None 30 | -------------------------------------------------------------------------------- /discord/ext/lava/types/objects/track.py: -------------------------------------------------------------------------------- 1 | from typing import Any, TypedDict 2 | 3 | 4 | class TrackInfoData(TypedDict): 5 | identifier: str 6 | isSeekable: bool 7 | author: str 8 | length: int 9 | isStream: bool 10 | position: int 11 | title: str 12 | uri: str | None 13 | artworkUrl: str | None 14 | isrc: str | None 15 | sourceName: str 16 | 17 | 18 | type TrackPluginInfoData = dict[str, Any] 19 | type TrackUserData = dict[str, Any] 20 | 21 | 22 | class TrackData(TypedDict): 23 | encoded: str 24 | info: TrackInfoData 25 | pluginInfo: TrackPluginInfoData 26 | userData: TrackUserData 27 | -------------------------------------------------------------------------------- /discord/ext/lava/types/rest.py: -------------------------------------------------------------------------------- 1 | from typing import Literal, Never, NotRequired, TypedDict 2 | 3 | from .common import ExceptionData, PlayerStateData 4 | from .objects.filters import FiltersData 5 | from .objects.playlist import PlaylistData 6 | from .objects.stats import StatsData 7 | from .objects.track import TrackData, TrackUserData 8 | 9 | 10 | ########## 11 | # Errors # 12 | ########## 13 | class ErrorRequestParameters(TypedDict): 14 | trace: NotRequired[bool] 15 | 16 | 17 | class ErrorData(TypedDict): 18 | timestamp: int 19 | status: int 20 | error: str 21 | trace: NotRequired[str] 22 | message: str 23 | path: str 24 | 25 | 26 | ########## 27 | # Common # 28 | ########## 29 | class VoiceStateData(TypedDict): 30 | token: str 31 | endpoint: str 32 | sessionId: str 33 | 34 | 35 | ######################################################################### 36 | # GET # /v4/sessions/{sessionId}/players # 37 | # GET # /v4/sessions/{sessionId}/players/{guildId} # 38 | # PATCH # /v4/sessions/{sessionId}/players/{guildId}?{noReplace=} # 39 | ######################################################################### 40 | class PlayerData(TypedDict): 41 | guildId: str 42 | track: TrackData | None 43 | volume: int 44 | paused: bool 45 | state: PlayerStateData 46 | voice: VoiceStateData 47 | filters: FiltersData 48 | 49 | 50 | ######################################################################### 51 | # PATCH # /v4/sessions/{sessionId}/players/{guildId}?{noReplace=} # 52 | ######################################################################### 53 | class UpdatePlayerRequestParameters(TypedDict): 54 | noReplace: NotRequired[bool] 55 | 56 | 57 | class UpdatePlayerRequestTrackData(TypedDict): 58 | encoded: NotRequired[str | None] 59 | identifier: NotRequired[str] 60 | userData: NotRequired[TrackUserData] 61 | 62 | 63 | class UpdatePlayerRequestData(TypedDict): 64 | track: NotRequired[UpdatePlayerRequestTrackData] 65 | position: NotRequired[int] 66 | endTime: NotRequired[int | None] 67 | volume: NotRequired[int] 68 | paused: NotRequired[bool] 69 | filters: NotRequired[FiltersData] 70 | voice: NotRequired[VoiceStateData] 71 | 72 | 73 | ##################################### 74 | # PATCH ## /v4/sessions/{sessionId} # 75 | ##################################### 76 | class UpdateSessionRequestData(TypedDict): 77 | resuming: NotRequired[bool] 78 | timeout: NotRequired[int] 79 | 80 | 81 | class SessionData(TypedDict): 82 | resuming: bool 83 | timeout: int 84 | 85 | 86 | ############################################ 87 | # GET ## /v4/loadtracks?{identifier=} # 88 | ############################################ 89 | class SearchRequestParameters(TypedDict): 90 | identifier: str 91 | 92 | 93 | class TrackResultData(TypedDict): 94 | loadType: Literal["track"] 95 | data: TrackData 96 | 97 | 98 | class PlaylistResultData(TypedDict): 99 | loadType: Literal["playlist"] 100 | data: PlaylistData 101 | 102 | 103 | class SearchResultData(TypedDict): 104 | loadType: Literal["search"] 105 | data: list[TrackData] 106 | 107 | 108 | class EmptyResultData(TypedDict): 109 | loadType: Literal["empty"] 110 | data: dict[Never, Never] 111 | 112 | 113 | class ErrorResultData(TypedDict): 114 | loadType: Literal["error"] 115 | data: ExceptionData 116 | 117 | 118 | type SearchData = TrackResultData | PlaylistResultData | SearchResultData | EmptyResultData | ErrorResultData 119 | 120 | 121 | ###################################################### 122 | # GET # /v4/decodetrack?{encodedTrack=} # 123 | ###################################################### 124 | class DecodeTrackRequestParameters(TypedDict): 125 | encodedTrack: str 126 | 127 | 128 | type DecodedTrackData = TrackData 129 | 130 | 131 | ############################ 132 | # POST ## /v4/decodetracks # 133 | ############################ 134 | type DecodeTracksRequestData = list[str] 135 | type DecodedTracksData = list[DecodedTrackData] 136 | 137 | 138 | ################## 139 | # GET # /v4/info # 140 | ################## 141 | class VersionData(TypedDict): 142 | semver: str 143 | major: int 144 | minor: int 145 | patch: int 146 | preRelease: str | None 147 | build: str | None 148 | 149 | 150 | class GitData(TypedDict): 151 | branch: str 152 | commit: str 153 | commitTime: int 154 | 155 | 156 | class PluginData(TypedDict): 157 | name: str 158 | version: str 159 | 160 | 161 | class LavalinkInfoData(TypedDict): 162 | version: VersionData 163 | buildTime: int 164 | git: GitData 165 | jvm: str 166 | lavaplayer: str 167 | sourceManagers: list[str] 168 | filters: list[str] 169 | plugins: list[PluginData] 170 | 171 | 172 | ################### 173 | # GET # /v4/stats # 174 | ################### 175 | type LavalinkStatsData = StatsData 176 | 177 | 178 | ################## 179 | # GET # /version # 180 | ################## 181 | type LavalinkVersionData = str 182 | 183 | 184 | ################################# 185 | # GET # /v4/routeplanner/status # 186 | ################################# 187 | type IpBlockType = Literal["Inet4Address", "Inet6Address"] 188 | 189 | 190 | class IpBlockData(TypedDict): 191 | type: IpBlockType 192 | size: str 193 | 194 | 195 | class FailingAddressData(TypedDict): 196 | failingAddress: str 197 | failingTimestamp: int 198 | failingTime: str 199 | 200 | 201 | class RotatingIpRoutePlannerDetailsData(TypedDict): 202 | ipBlock: IpBlockData 203 | failingAddresses: list[FailingAddressData] 204 | rotateIndex: str 205 | ipIndex: str 206 | currentAddress: str 207 | 208 | 209 | RotatingIpRoutePlannerStatusData = TypedDict( 210 | "RotatingIpRoutePlannerStatusData", { 211 | "class": Literal["RotatingIpRoutePlanner"], 212 | "details": RotatingIpRoutePlannerDetailsData 213 | } 214 | ) 215 | 216 | 217 | class NanoIpRoutePlannerDetailsData(TypedDict): 218 | ipBlock: IpBlockData 219 | failingAddresses: list[FailingAddressData] 220 | currentAddressIndex: str 221 | 222 | 223 | NanoIpRoutePlannerStatusData = TypedDict( 224 | "NanoIpRoutePlannerStatusData", { 225 | "class": Literal["NanoIpRoutePlanner"], 226 | "details": NanoIpRoutePlannerDetailsData 227 | } 228 | ) 229 | 230 | 231 | class RotatingNanoIpRoutePlannerDetailsData(TypedDict): 232 | ipBlock: IpBlockData 233 | failingAddresses: list[FailingAddressData] 234 | currentAddressIndex: str 235 | blockIndex: str 236 | 237 | 238 | RotatingNanoIpRoutePlannerStatusData = TypedDict( 239 | "RotatingNanoIpRoutePlannerStatusData", { 240 | "class": Literal["RotatingNanoIpRoutePlanner"], 241 | "details": RotatingNanoIpRoutePlannerDetailsData 242 | } 243 | ) 244 | 245 | 246 | class BalancingIpRoutePlannerDetailsData(TypedDict): 247 | ipBlock: IpBlockData 248 | failingAddresses: list[FailingAddressData] 249 | 250 | 251 | BalancingIpRoutePlannerStatusData = TypedDict( 252 | "BalancingIpRoutePlannerStatusData", { 253 | "class": Literal["BalancingIpRoutePlanner"], 254 | "details": BalancingIpRoutePlannerDetailsData 255 | } 256 | ) 257 | 258 | NoRoutePlannerStatusData = TypedDict( 259 | "NoRoutePlannerStatusData", { 260 | "class": None, 261 | "details": None 262 | } 263 | ) 264 | 265 | type RoutePlannerStatus = ( 266 | RotatingIpRoutePlannerStatusData 267 | | NanoIpRoutePlannerStatusData 268 | | RotatingNanoIpRoutePlannerStatusData 269 | | BalancingIpRoutePlannerStatusData 270 | | NoRoutePlannerStatusData 271 | ) 272 | 273 | 274 | ######################################## 275 | # POST # /v4/routeplanner/free/address # 276 | ######################################## 277 | class FreeAddressRequestData(TypedDict): 278 | address: str 279 | 280 | 281 | ######### 282 | # Types # 283 | ######### 284 | type RequestMethod = Literal["GET", "HEAD", "POST", "PUT", "DELETE", "PATCH"] 285 | RequestHeaders = TypedDict( 286 | "RequestHeaders", { 287 | "Authorization": str, 288 | "Content-Type": NotRequired[Literal["application/json"]] 289 | } 290 | ) 291 | type RequestParameters = ( 292 | ErrorRequestParameters | UpdatePlayerRequestParameters | SearchRequestParameters | DecodeTrackRequestParameters 293 | ) 294 | type RequestData = ( 295 | UpdatePlayerRequestData | UpdateSessionRequestData | DecodeTracksRequestData | FreeAddressRequestData 296 | ) 297 | 298 | 299 | class RequestKwargs(TypedDict): 300 | headers: RequestHeaders 301 | params: NotRequired[RequestParameters | None] 302 | data: NotRequired[str | None] 303 | -------------------------------------------------------------------------------- /discord/ext/lava/types/websocket.py: -------------------------------------------------------------------------------- 1 | from typing import Literal, TypedDict 2 | 3 | from .common import PlayerStateData 4 | from .objects.events import EventData 5 | from .objects.stats import StatsData 6 | 7 | 8 | # 'ready' payload 9 | class ReadyPayload(TypedDict): 10 | op: Literal["ready"] 11 | resumed: bool 12 | sessionId: str 13 | 14 | 15 | # 'playerUpdate' payload 16 | class PlayerUpdatePayload(TypedDict): 17 | op: Literal["playerUpdate"] 18 | guildId: str 19 | state: PlayerStateData 20 | 21 | 22 | # 'stats' payload 23 | class StatsPayload(StatsData): 24 | op: Literal["stats"] 25 | 26 | 27 | # 'event' payload 28 | type EventPayload = EventData 29 | 30 | # payload 31 | type Payload = ReadyPayload | PlayerUpdatePayload | StatsPayload | EventPayload 32 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import sys 4 | from typing import Any 5 | 6 | 7 | _DISCORD: str = "https://discord.com/invite/w9f6NkQbde" 8 | _DISCORD_SVG: str = """ 9 | 11 | 14 | 23 | 24 | """ 25 | 26 | _GITHUB: str = "https://github.com/aaronhnsy/discord-ext-lava" 27 | _GITHUB_SVG: str = """ 28 | 29 | 35 | 36 | """ 37 | 38 | 39 | ####################### 40 | # Project Information # 41 | ####################### 42 | 43 | project: str = "discord-ext-lava" 44 | copyright: str = "2020 - Present, Aaron Hennessey" 45 | author: str = "Aaron Hennessey" 46 | 47 | with open(os.path.abspath(os.path.join(os.path.dirname(__file__), "../discord/ext/lava/__init__.py"))) as file: 48 | 49 | if not (match := re.search(r"^__version__: [^=]* = \"([^\"]*)\"", file.read(), re.MULTILINE)): 50 | raise RuntimeError 51 | 52 | _VERSION: str = match[1] 53 | version: str = _VERSION 54 | release: str = _VERSION 55 | 56 | 57 | ######################### 58 | # General Configuration # 59 | ######################### 60 | 61 | sys.path.insert(0, os.path.abspath("..")) 62 | sys.path.append(os.path.abspath("extensions")) 63 | 64 | extensions: list[str] = [ 65 | "sphinx.ext.autodoc", 66 | "sphinx.ext.napoleon", 67 | "sphinx.ext.intersphinx", 68 | "sphinx.ext.extlinks", 69 | "sphinxcontrib_trio", 70 | "resource_links", 71 | "sphinx_copybutton", 72 | "sphinx_inline_tabs" 73 | ] 74 | exclude_patters: list[str] = [ 75 | "_build", 76 | "Thumbs.db", 77 | ".DS_Store" 78 | ] 79 | 80 | needs_sphinx: str = "5.1.1" 81 | nitpicky: bool = True 82 | 83 | 84 | ########################### 85 | # Options for HTML output # 86 | ########################### 87 | 88 | html_theme: str = "furo" 89 | 90 | html_theme_options: dict[str, Any] = { 91 | "footer_icons": [ 92 | { 93 | "name": "Discord", 94 | "url": _DISCORD, 95 | "html": _DISCORD_SVG, 96 | "class": "", 97 | }, 98 | { 99 | "name": "GitHub", 100 | "url": _GITHUB, 101 | "html": _GITHUB_SVG, 102 | "class": "", 103 | }, 104 | ], 105 | } 106 | html_title: str = "discord.ext.lava" 107 | 108 | html_css_files: list[str] = [ 109 | "custom.css", 110 | ] 111 | html_static_path: list[str] = [ 112 | "static" 113 | ] 114 | 115 | 116 | ############## 117 | # Extensions # 118 | ############## 119 | 120 | # autodoc 121 | autoclass_content: str = "class" 122 | autodoc_class_signature: str = "mixed" 123 | autodoc_member_order: str = "bysource" 124 | autodoc_default_options: dict[str, Any] = { 125 | "undoc-members": True 126 | } 127 | autodoc_typehints: str = "signature" 128 | autodoc_type_aliases: dict[str, str] = { 129 | "VoiceChannel": "~discord.VoiceChannel | ~discord.StageChannel", 130 | "BotT": "~discord.ext.commands.Bot", 131 | "ContextT": "~discord.ext.commands.Context", 132 | "PlayerT": "~discord.ext.lava.Player", 133 | "QueueItemT": "~discord.ext.lava.Track", 134 | } 135 | autodoc_typehints_format: str = "short" 136 | 137 | # napoleon 138 | napoleon_use_admonition_for_examples: bool = True 139 | napoleon_use_admonition_for_notes: bool = True 140 | 141 | # intersphinx 142 | intersphinx_mapping: dict[str, tuple[str, None]] = { 143 | "aiohttp": ("https://docs.aiohttp.org/en/stable/", None), 144 | "python": ("https://docs.python.org/3.10", None), 145 | "discord": ("https://discordpy.readthedocs.io/en/latest", None), 146 | } 147 | 148 | # ext links 149 | extlinks: dict[str, tuple[str, str]] = { 150 | "issue": (f"{_GITHUB}/issues/%s", "GH-"), 151 | } 152 | extlinks_detect_hardcoded_links: bool = True 153 | 154 | # resource links 155 | resource_links: dict[str, str] = { 156 | "github": _GITHUB, 157 | "issues": f"{_GITHUB}/issues", 158 | "discussions": f"{_GITHUB}/discussions", 159 | 160 | "examples": f"{_GITHUB}/tree/main/examples", 161 | 162 | "discord": _DISCORD, 163 | 164 | "discord.py": "https://github.com/Rapptz/discord.py", 165 | "lavalink": "https://github.com/freyacodes/Lavalink", 166 | "obsidian": "https://github.com/mixtape-bot/obsidian/tree/v2", 167 | } 168 | -------------------------------------------------------------------------------- /docs/extensions/resource_links.py: -------------------------------------------------------------------------------- 1 | """ 2 | https://github.com/Rapptz/discord.py/blob/0bcb0d0e3ce395d42a5b1dae61b0090791ee018d/docs/extensions/resourcelinks.py 3 | 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2015-present Rapptz 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a 9 | copy of this software and associated documentation files (the "Software"), 10 | to deal in the Software without restriction, including without limitation 11 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 12 | and/or sell copies of the Software, and to permit persons to whom the 13 | Software is furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 24 | DEALINGS IN THE SOFTWARE. 25 | """ 26 | 27 | from typing import Any 28 | 29 | import sphinx 30 | from docutils import nodes, utils 31 | from docutils.nodes import Node, system_message 32 | from docutils.parsers.rst.states import Inliner 33 | from sphinx.application import Sphinx 34 | from sphinx.util.nodes import split_explicit_title 35 | from sphinx.util.typing import RoleFunction 36 | 37 | 38 | def make_link_role(resource_links: dict[str, str]) -> RoleFunction: 39 | 40 | # noinspection PyUnusedLocal 41 | def role( 42 | typ: str, 43 | rawtext: str, 44 | text: str, 45 | lineno: int, 46 | inliner: Inliner, 47 | options: dict[Any, Any] | None = None, 48 | content: list[str] | None = None 49 | ) -> tuple[list[Node], list[system_message]]: 50 | 51 | if options is None: 52 | options = {} 53 | if content is None: 54 | content = [] 55 | 56 | has_explicit_title, title, key = split_explicit_title(utils.unescape(text)) 57 | full_url = resource_links[key] 58 | 59 | if not has_explicit_title: 60 | title = full_url 61 | 62 | pnode = nodes.reference(title, title, internal=False, refuri=full_url) 63 | return [pnode], [] 64 | 65 | return role 66 | 67 | 68 | def add_link_role(app: Sphinx) -> None: 69 | app.add_role("resource", make_link_role(app.config.resource_links)) 70 | 71 | 72 | def setup(app: Sphinx) -> dict[str, Any]: 73 | app.add_config_value("resource_links", {}, "env") 74 | app.connect("builder-inited", add_link_role) 75 | return {"version": sphinx.__display_version__, "parallel_read_safe": True} 76 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. py:currentmodule:: discord.ext.lava 2 | 3 | 4 | discord-ext-lava 5 | ================ 6 | A discord.py extension for lavaplayer-based audio nodes. 7 | 8 | 9 | .. toctree:: 10 | :hidden: 11 | :caption: Introduction 12 | 13 | pages/installation 14 | pages/contribution 15 | 16 | 17 | .. toctree:: 18 | :hidden: 19 | :caption: API 20 | 21 | pages/usage 22 | pages/reference 23 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | %SPHINXBUILD% >NUL 2>NUL 14 | if errorlevel 9009 ( 15 | echo. 16 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 17 | echo.installed, then set the SPHINXBUILD environment variable to point 18 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 19 | echo.may add the Sphinx directory to PATH. 20 | echo. 21 | echo.If you don't have Sphinx installed, grab it from 22 | echo.https://www.sphinx-doc.org/ 23 | exit /b 1 24 | ) 25 | 26 | if "%1" == "" goto help 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/pages/contribution.rst: -------------------------------------------------------------------------------- 1 | Contribution Guide 2 | ================== 3 | -------------------------------------------------------------------------------- /docs/pages/installation.rst: -------------------------------------------------------------------------------- 1 | Installation 2 | ============ 3 | 4 | Stable 5 | ------ 6 | .. tab:: Latest 7 | 8 | .. code-block:: shell 9 | 10 | pip install -U discord-ext-lava 11 | 12 | .. tab:: Specific Version 13 | 14 | .. code-block:: shell 15 | 16 | pip install -U discord-ext-lava==0.5.1 17 | 18 | .. tab:: Force Reinstall 19 | 20 | .. code-block:: shell 21 | 22 | pip install --force-reinstall discord-ext-lava 23 | 24 | 25 | Development 26 | ----------- 27 | .. tab:: Main 28 | 29 | .. code-block:: shell 30 | 31 | pip install -U git+https://github.com/aaronhnsy/discord-ext-lava.git@main 32 | 33 | .. tab:: Branch 34 | 35 | .. code-block:: shell 36 | 37 | pip install -U git+https://github.com/aaronhnsy/discord-ext-lava.git@v0.4.x 38 | 39 | .. tab:: Tag 40 | 41 | .. code-block:: shell 42 | 43 | pip install -U git+https://github.com/aaronhnsy/discord-ext-lava.git@v0.5.0 44 | 45 | 46 | .. tab:: Commit 47 | 48 | .. code-block:: shell 49 | 50 | pip install -U git+https://github.com/aaronhnsy/discord-ext-lava.git@1ce8768 51 | -------------------------------------------------------------------------------- /docs/pages/reference.rst: -------------------------------------------------------------------------------- 1 | .. py:currentmodule:: discord.ext.lava 2 | 3 | 4 | Reference 5 | ========= 6 | 7 | Pool 8 | ---- 9 | .. autoclass:: Pool 10 | :members: 11 | 12 | Node 13 | ---- 14 | .. autoclass:: discord.ext.lava.Node 15 | :members: 16 | 17 | Player 18 | ------ 19 | .. autoclass:: discord.ext.lava.Player 20 | :members: 21 | :exclude-members: on_voice_server_update, on_voice_state_update 22 | 23 | Queue 24 | ----- 25 | .. autoclass:: discord.ext.lava.Queue 26 | :members: 27 | 28 | 29 | Objects 30 | ------- 31 | 32 | Track 33 | ~~~~~ 34 | .. autoclass:: discord.ext.lava.Track 35 | :members: 36 | 37 | Collection 38 | ~~~~~~~~~~ 39 | .. autoclass:: discord.ext.lava.Collection 40 | :members: 41 | 42 | Search 43 | ~~~~~~ 44 | .. autoclass:: discord.ext.lava.Search 45 | :members: 46 | 47 | 48 | Events 49 | ------ 50 | 51 | TrackStart 52 | ~~~~~~~~~~ 53 | .. autoclass:: discord.ext.lava.TrackStart 54 | :members: 55 | 56 | TrackEnd 57 | ~~~~~~~~ 58 | .. autoclass:: discord.ext.lava.TrackEnd 59 | :members: 60 | 61 | TrackStuck 62 | ~~~~~~~~~~ 63 | .. autoclass:: discord.ext.lava.TrackStuck 64 | :members: 65 | 66 | TrackException 67 | ~~~~~~~~~~~~~~ 68 | .. autoclass:: discord.ext.lava.TrackException 69 | :members: 70 | 71 | WebsocketOpen 72 | ~~~~~~~~~~~~~ 73 | .. autoclass:: discord.ext.lava.WebsocketOpen 74 | :members: 75 | 76 | WebsocketClosed 77 | ~~~~~~~~~~~~~~~ 78 | .. autoclass:: discord.ext.lava.WebsocketClosed 79 | :members: 80 | 81 | 82 | Filters 83 | ------- 84 | 85 | ChannelMix 86 | ~~~~~~~~~~ 87 | .. autoclass:: discord.ext.lava.ChannelMix 88 | :members: 89 | 90 | Distortion 91 | ~~~~~~~~~~ 92 | .. autoclass:: discord.ext.lava.Distortion 93 | :members: 94 | 95 | Equalizer 96 | ~~~~~~~~~ 97 | .. autoclass:: discord.ext.lava.Equalizer 98 | :members: 99 | 100 | Karaoke 101 | ~~~~~~~ 102 | .. autoclass:: discord.ext.lava.Karaoke 103 | :members: 104 | 105 | LowPass 106 | ~~~~~~~ 107 | .. autoclass:: discord.ext.lava.LowPass 108 | :members: 109 | 110 | Rotation 111 | ~~~~~~~~ 112 | .. autoclass:: discord.ext.lava.Rotation 113 | :members: 114 | 115 | Timescale 116 | ~~~~~~~~~ 117 | .. autoclass:: discord.ext.lava.Timescale 118 | :members: 119 | 120 | Tremolo 121 | ~~~~~~~ 122 | .. autoclass:: discord.ext.lava.Tremolo 123 | :members: 124 | 125 | Vibrato 126 | ~~~~~~~ 127 | .. autoclass:: discord.ext.lava.Vibrato 128 | :members: 129 | 130 | Volume 131 | ~~~~~~ 132 | .. autoclass:: discord.ext.lava.Volume 133 | :members: 134 | 135 | Filter 136 | ~~~~~~ 137 | .. autoclass:: discord.ext.lava.Filter 138 | :members: 139 | 140 | 141 | Enums 142 | ----- 143 | 144 | Provider 145 | ~~~~~~~~ 146 | .. autoclass:: discord.ext.lava.Provider 147 | :members: 148 | 149 | QueueLoopMode 150 | ~~~~~~~~~~~~~ 151 | .. autoclass:: discord.ext.lava.QueueLoopMode 152 | :members: 153 | 154 | Source 155 | ~~~~~~ 156 | .. autoclass:: discord.ext.lava.Source 157 | :members: 158 | 159 | 160 | Exceptions 161 | ---------- 162 | 163 | LavaError 164 | ~~~~~~~~~~ 165 | .. autoexception:: discord.ext.lava.LavaError 166 | 167 | NodeAlreadyExists 168 | ~~~~~~~~~~~~~~~~~ 169 | .. autoexception:: discord.ext.lava.NodeAlreadyExists 170 | 171 | NodeNotFound 172 | ~~~~~~~~~~~~ 173 | .. autoexception:: discord.ext.lava.NodeNotFound 174 | 175 | NoNodesConnected 176 | ~~~~~~~~~~~~~~~~ 177 | .. autoexception:: discord.ext.lava.NoNodesConnected 178 | 179 | NodeAlreadyConnected 180 | ~~~~~~~~~~~~~~~~~~~~ 181 | .. autoexception:: discord.ext.lava.NodeAlreadyConnected 182 | 183 | NodeConnectionError 184 | ~~~~~~~~~~~~~~~~~~~ 185 | .. autoexception:: discord.ext.lava.NodeConnectionError 186 | 187 | InvalidPassword 188 | ~~~~~~~~~~~~~~~ 189 | .. autoexception:: discord.ext.lava.InvalidPassword 190 | 191 | NodeNotConnected 192 | ~~~~~~~~~~~~~~~~ 193 | .. autoexception:: discord.ext.lava.NodeNotConnected 194 | 195 | HTTPError 196 | ~~~~~~~~~ 197 | .. autoexception:: discord.ext.lava.HTTPError 198 | :members: 199 | 200 | NoResultsFound 201 | ~~~~~~~~~~~~~~ 202 | .. autoexception:: discord.ext.lava.NoResultsFound 203 | :members: 204 | 205 | SearchFailed 206 | ~~~~~~~~~~~~ 207 | .. autoexception:: discord.ext.lava.SearchFailed 208 | :members: 209 | 210 | 211 | Utils 212 | ----- 213 | 214 | SPOTIFY_URL_REGEX 215 | ~~~~~~~~~~~~~~~~~ 216 | .. attribute:: discord.ext.lava.SPOTIFY_URL_REGEX 217 | 218 | A regex that matches spotify URLs for tracks, albums, playlists, and artists. 219 | 220 | 221 | MISSING 222 | ~~~~~~~ 223 | .. attribute:: discord.ext.lava.MISSING 224 | 225 | A sentinel value that is used to indicate a missing value with distinction from :class:`None`. 226 | -------------------------------------------------------------------------------- /docs/pages/usage.rst: -------------------------------------------------------------------------------- 1 | .. py:currentmodule:: discord.ext.lava 2 | 3 | 4 | Usage 5 | ===== 6 | -------------------------------------------------------------------------------- /docs/static/custom.css: -------------------------------------------------------------------------------- 1 | .sig-param::before { 2 | content: "\a"; 3 | white-space: pre; 4 | } 5 | 6 | dt em.sig-param:last-of-type::after { 7 | content: "\a"; 8 | white-space: pre; 9 | } 10 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. 2 | 3 | [[package]] 4 | name = "aiohttp" 5 | version = "3.9.3" 6 | description = "Async http client/server framework (asyncio)" 7 | optional = false 8 | python-versions = ">=3.8" 9 | files = [ 10 | {file = "aiohttp-3.9.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:939677b61f9d72a4fa2a042a5eee2a99a24001a67c13da113b2e30396567db54"}, 11 | {file = "aiohttp-3.9.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1f5cd333fcf7590a18334c90f8c9147c837a6ec8a178e88d90a9b96ea03194cc"}, 12 | {file = "aiohttp-3.9.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:82e6aa28dd46374f72093eda8bcd142f7771ee1eb9d1e223ff0fa7177a96b4a5"}, 13 | {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f56455b0c2c7cc3b0c584815264461d07b177f903a04481dfc33e08a89f0c26b"}, 14 | {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bca77a198bb6e69795ef2f09a5f4c12758487f83f33d63acde5f0d4919815768"}, 15 | {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e083c285857b78ee21a96ba1eb1b5339733c3563f72980728ca2b08b53826ca5"}, 16 | {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab40e6251c3873d86ea9b30a1ac6d7478c09277b32e14745d0d3c6e76e3c7e29"}, 17 | {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df822ee7feaaeffb99c1a9e5e608800bd8eda6e5f18f5cfb0dc7eeb2eaa6bbec"}, 18 | {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:acef0899fea7492145d2bbaaaec7b345c87753168589cc7faf0afec9afe9b747"}, 19 | {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cd73265a9e5ea618014802ab01babf1940cecb90c9762d8b9e7d2cc1e1969ec6"}, 20 | {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:a78ed8a53a1221393d9637c01870248a6f4ea5b214a59a92a36f18151739452c"}, 21 | {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:6b0e029353361f1746bac2e4cc19b32f972ec03f0f943b390c4ab3371840aabf"}, 22 | {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7cf5c9458e1e90e3c390c2639f1017a0379a99a94fdfad3a1fd966a2874bba52"}, 23 | {file = "aiohttp-3.9.3-cp310-cp310-win32.whl", hash = "sha256:3e59c23c52765951b69ec45ddbbc9403a8761ee6f57253250c6e1536cacc758b"}, 24 | {file = "aiohttp-3.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:055ce4f74b82551678291473f66dc9fb9048a50d8324278751926ff0ae7715e5"}, 25 | {file = "aiohttp-3.9.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6b88f9386ff1ad91ace19d2a1c0225896e28815ee09fc6a8932fded8cda97c3d"}, 26 | {file = "aiohttp-3.9.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c46956ed82961e31557b6857a5ca153c67e5476972e5f7190015018760938da2"}, 27 | {file = "aiohttp-3.9.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:07b837ef0d2f252f96009e9b8435ec1fef68ef8b1461933253d318748ec1acdc"}, 28 | {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad46e6f620574b3b4801c68255492e0159d1712271cc99d8bdf35f2043ec266"}, 29 | {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ed3e046ea7b14938112ccd53d91c1539af3e6679b222f9469981e3dac7ba1ce"}, 30 | {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:039df344b45ae0b34ac885ab5b53940b174530d4dd8a14ed8b0e2155b9dddccb"}, 31 | {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7943c414d3a8d9235f5f15c22ace69787c140c80b718dcd57caaade95f7cd93b"}, 32 | {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84871a243359bb42c12728f04d181a389718710129b36b6aad0fc4655a7647d4"}, 33 | {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5eafe2c065df5401ba06821b9a054d9cb2848867f3c59801b5d07a0be3a380ae"}, 34 | {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9d3c9b50f19704552f23b4eaea1fc082fdd82c63429a6506446cbd8737823da3"}, 35 | {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:f033d80bc6283092613882dfe40419c6a6a1527e04fc69350e87a9df02bbc283"}, 36 | {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:2c895a656dd7e061b2fd6bb77d971cc38f2afc277229ce7dd3552de8313a483e"}, 37 | {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1f5a71d25cd8106eab05f8704cd9167b6e5187bcdf8f090a66c6d88b634802b4"}, 38 | {file = "aiohttp-3.9.3-cp311-cp311-win32.whl", hash = "sha256:50fca156d718f8ced687a373f9e140c1bb765ca16e3d6f4fe116e3df7c05b2c5"}, 39 | {file = "aiohttp-3.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:5fe9ce6c09668063b8447f85d43b8d1c4e5d3d7e92c63173e6180b2ac5d46dd8"}, 40 | {file = "aiohttp-3.9.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:38a19bc3b686ad55804ae931012f78f7a534cce165d089a2059f658f6c91fa60"}, 41 | {file = "aiohttp-3.9.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:770d015888c2a598b377bd2f663adfd947d78c0124cfe7b959e1ef39f5b13869"}, 42 | {file = "aiohttp-3.9.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ee43080e75fc92bf36219926c8e6de497f9b247301bbf88c5c7593d931426679"}, 43 | {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52df73f14ed99cee84865b95a3d9e044f226320a87af208f068ecc33e0c35b96"}, 44 | {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc9b311743a78043b26ffaeeb9715dc360335e5517832f5a8e339f8a43581e4d"}, 45 | {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b955ed993491f1a5da7f92e98d5dad3c1e14dc175f74517c4e610b1f2456fb11"}, 46 | {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:504b6981675ace64c28bf4a05a508af5cde526e36492c98916127f5a02354d53"}, 47 | {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a6fe5571784af92b6bc2fda8d1925cccdf24642d49546d3144948a6a1ed58ca5"}, 48 | {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ba39e9c8627edc56544c8628cc180d88605df3892beeb2b94c9bc857774848ca"}, 49 | {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e5e46b578c0e9db71d04c4b506a2121c0cb371dd89af17a0586ff6769d4c58c1"}, 50 | {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:938a9653e1e0c592053f815f7028e41a3062e902095e5a7dc84617c87267ebd5"}, 51 | {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:c3452ea726c76e92f3b9fae4b34a151981a9ec0a4847a627c43d71a15ac32aa6"}, 52 | {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ff30218887e62209942f91ac1be902cc80cddb86bf00fbc6783b7a43b2bea26f"}, 53 | {file = "aiohttp-3.9.3-cp312-cp312-win32.whl", hash = "sha256:38f307b41e0bea3294a9a2a87833191e4bcf89bb0365e83a8be3a58b31fb7f38"}, 54 | {file = "aiohttp-3.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:b791a3143681a520c0a17e26ae7465f1b6f99461a28019d1a2f425236e6eedb5"}, 55 | {file = "aiohttp-3.9.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0ed621426d961df79aa3b963ac7af0d40392956ffa9be022024cd16297b30c8c"}, 56 | {file = "aiohttp-3.9.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7f46acd6a194287b7e41e87957bfe2ad1ad88318d447caf5b090012f2c5bb528"}, 57 | {file = "aiohttp-3.9.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:feeb18a801aacb098220e2c3eea59a512362eb408d4afd0c242044c33ad6d542"}, 58 | {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f734e38fd8666f53da904c52a23ce517f1b07722118d750405af7e4123933511"}, 59 | {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b40670ec7e2156d8e57f70aec34a7216407848dfe6c693ef131ddf6e76feb672"}, 60 | {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fdd215b7b7fd4a53994f238d0f46b7ba4ac4c0adb12452beee724ddd0743ae5d"}, 61 | {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:017a21b0df49039c8f46ca0971b3a7fdc1f56741ab1240cb90ca408049766168"}, 62 | {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e99abf0bba688259a496f966211c49a514e65afa9b3073a1fcee08856e04425b"}, 63 | {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:648056db9a9fa565d3fa851880f99f45e3f9a771dd3ff3bb0c048ea83fb28194"}, 64 | {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8aacb477dc26797ee089721536a292a664846489c49d3ef9725f992449eda5a8"}, 65 | {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:522a11c934ea660ff8953eda090dcd2154d367dec1ae3c540aff9f8a5c109ab4"}, 66 | {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:5bce0dc147ca85caa5d33debc4f4d65e8e8b5c97c7f9f660f215fa74fc49a321"}, 67 | {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b4af9f25b49a7be47c0972139e59ec0e8285c371049df1a63b6ca81fdd216a2"}, 68 | {file = "aiohttp-3.9.3-cp38-cp38-win32.whl", hash = "sha256:298abd678033b8571995650ccee753d9458dfa0377be4dba91e4491da3f2be63"}, 69 | {file = "aiohttp-3.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:69361bfdca5468c0488d7017b9b1e5ce769d40b46a9f4a2eed26b78619e9396c"}, 70 | {file = "aiohttp-3.9.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0fa43c32d1643f518491d9d3a730f85f5bbaedcbd7fbcae27435bb8b7a061b29"}, 71 | {file = "aiohttp-3.9.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:835a55b7ca49468aaaac0b217092dfdff370e6c215c9224c52f30daaa735c1c1"}, 72 | {file = "aiohttp-3.9.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06a9b2c8837d9a94fae16c6223acc14b4dfdff216ab9b7202e07a9a09541168f"}, 73 | {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abf151955990d23f84205286938796c55ff11bbfb4ccfada8c9c83ae6b3c89a3"}, 74 | {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59c26c95975f26e662ca78fdf543d4eeaef70e533a672b4113dd888bd2423caa"}, 75 | {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f95511dd5d0e05fd9728bac4096319f80615aaef4acbecb35a990afebe953b0e"}, 76 | {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:595f105710293e76b9dc09f52e0dd896bd064a79346234b521f6b968ffdd8e58"}, 77 | {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7c8b816c2b5af5c8a436df44ca08258fc1a13b449393a91484225fcb7545533"}, 78 | {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f1088fa100bf46e7b398ffd9904f4808a0612e1d966b4aa43baa535d1b6341eb"}, 79 | {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f59dfe57bb1ec82ac0698ebfcdb7bcd0e99c255bd637ff613760d5f33e7c81b3"}, 80 | {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:361a1026c9dd4aba0109e4040e2aecf9884f5cfe1b1b1bd3d09419c205e2e53d"}, 81 | {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:363afe77cfcbe3a36353d8ea133e904b108feea505aa4792dad6585a8192c55a"}, 82 | {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e2c45c208c62e955e8256949eb225bd8b66a4c9b6865729a786f2aa79b72e9d"}, 83 | {file = "aiohttp-3.9.3-cp39-cp39-win32.whl", hash = "sha256:f7217af2e14da0856e082e96ff637f14ae45c10a5714b63c77f26d8884cf1051"}, 84 | {file = "aiohttp-3.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:27468897f628c627230dba07ec65dc8d0db566923c48f29e084ce382119802bc"}, 85 | {file = "aiohttp-3.9.3.tar.gz", hash = "sha256:90842933e5d1ff760fae6caca4b2b3edba53ba8f4b71e95dacf2818a2aca06f7"}, 86 | ] 87 | 88 | [package.dependencies] 89 | aiosignal = ">=1.1.2" 90 | attrs = ">=17.3.0" 91 | frozenlist = ">=1.1.1" 92 | multidict = ">=4.5,<7.0" 93 | yarl = ">=1.0,<2.0" 94 | 95 | [package.extras] 96 | speedups = ["Brotli", "aiodns", "brotlicffi"] 97 | 98 | [[package]] 99 | name = "aiosignal" 100 | version = "1.3.1" 101 | description = "aiosignal: a list of registered asynchronous callbacks" 102 | optional = false 103 | python-versions = ">=3.7" 104 | files = [ 105 | {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, 106 | {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, 107 | ] 108 | 109 | [package.dependencies] 110 | frozenlist = ">=1.1.0" 111 | 112 | [[package]] 113 | name = "alabaster" 114 | version = "0.7.16" 115 | description = "A light, configurable Sphinx theme" 116 | optional = false 117 | python-versions = ">=3.9" 118 | files = [ 119 | {file = "alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92"}, 120 | {file = "alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65"}, 121 | ] 122 | 123 | [[package]] 124 | name = "astunparse" 125 | version = "1.6.3" 126 | description = "An AST unparser for Python" 127 | optional = false 128 | python-versions = "*" 129 | files = [ 130 | {file = "astunparse-1.6.3-py2.py3-none-any.whl", hash = "sha256:c2652417f2c8b5bb325c885ae329bdf3f86424075c4fd1a128674bc6fba4b8e8"}, 131 | {file = "astunparse-1.6.3.tar.gz", hash = "sha256:5ad93a8456f0d084c3456d059fd9a92cce667963232cbf763eac3bc5b7940872"}, 132 | ] 133 | 134 | [package.dependencies] 135 | six = ">=1.6.1,<2.0" 136 | wheel = ">=0.23.0,<1.0" 137 | 138 | [[package]] 139 | name = "attrs" 140 | version = "23.2.0" 141 | description = "Classes Without Boilerplate" 142 | optional = false 143 | python-versions = ">=3.7" 144 | files = [ 145 | {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, 146 | {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, 147 | ] 148 | 149 | [package.extras] 150 | cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] 151 | dev = ["attrs[tests]", "pre-commit"] 152 | docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] 153 | tests = ["attrs[tests-no-zope]", "zope-interface"] 154 | tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] 155 | tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] 156 | 157 | [[package]] 158 | name = "babel" 159 | version = "2.14.0" 160 | description = "Internationalization utilities" 161 | optional = false 162 | python-versions = ">=3.7" 163 | files = [ 164 | {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"}, 165 | {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, 166 | ] 167 | 168 | [package.extras] 169 | dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] 170 | 171 | [[package]] 172 | name = "beautifulsoup4" 173 | version = "4.12.3" 174 | description = "Screen-scraping library" 175 | optional = false 176 | python-versions = ">=3.6.0" 177 | files = [ 178 | {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"}, 179 | {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"}, 180 | ] 181 | 182 | [package.dependencies] 183 | soupsieve = ">1.2" 184 | 185 | [package.extras] 186 | cchardet = ["cchardet"] 187 | chardet = ["chardet"] 188 | charset-normalizer = ["charset-normalizer"] 189 | html5lib = ["html5lib"] 190 | lxml = ["lxml"] 191 | 192 | [[package]] 193 | name = "braceexpand" 194 | version = "0.1.7" 195 | description = "Bash-style brace expansion for Python" 196 | optional = false 197 | python-versions = "*" 198 | files = [ 199 | {file = "braceexpand-0.1.7-py2.py3-none-any.whl", hash = "sha256:91332d53de7828103dcae5773fb43bc34950b0c8160e35e0f44c4427a3b85014"}, 200 | {file = "braceexpand-0.1.7.tar.gz", hash = "sha256:e6e539bd20eaea53547472ff94f4fb5c3d3bf9d0a89388c4b56663aba765f705"}, 201 | ] 202 | 203 | [[package]] 204 | name = "certifi" 205 | version = "2023.11.17" 206 | description = "Python package for providing Mozilla's CA Bundle." 207 | optional = false 208 | python-versions = ">=3.6" 209 | files = [ 210 | {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"}, 211 | {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"}, 212 | ] 213 | 214 | [[package]] 215 | name = "charset-normalizer" 216 | version = "3.3.2" 217 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." 218 | optional = false 219 | python-versions = ">=3.7.0" 220 | files = [ 221 | {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, 222 | {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, 223 | {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, 224 | {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, 225 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, 226 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, 227 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, 228 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, 229 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, 230 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, 231 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, 232 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, 233 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, 234 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, 235 | {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, 236 | {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, 237 | {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, 238 | {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, 239 | {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, 240 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, 241 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, 242 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, 243 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, 244 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, 245 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, 246 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, 247 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, 248 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, 249 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, 250 | {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, 251 | {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, 252 | {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, 253 | {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, 254 | {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, 255 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, 256 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, 257 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, 258 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, 259 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, 260 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, 261 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, 262 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, 263 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, 264 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, 265 | {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, 266 | {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, 267 | {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, 268 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, 269 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, 270 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, 271 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, 272 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, 273 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, 274 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, 275 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, 276 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, 277 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, 278 | {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, 279 | {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, 280 | {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, 281 | {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, 282 | {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, 283 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, 284 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, 285 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, 286 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, 287 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, 288 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, 289 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, 290 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, 291 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, 292 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, 293 | {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, 294 | {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, 295 | {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, 296 | {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, 297 | {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, 298 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, 299 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, 300 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, 301 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, 302 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, 303 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, 304 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, 305 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, 306 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, 307 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, 308 | {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, 309 | {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, 310 | {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, 311 | ] 312 | 313 | [[package]] 314 | name = "click" 315 | version = "8.1.7" 316 | description = "Composable command line interface toolkit" 317 | optional = false 318 | python-versions = ">=3.7" 319 | files = [ 320 | {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, 321 | {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, 322 | ] 323 | 324 | [package.dependencies] 325 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 326 | 327 | [[package]] 328 | name = "colorama" 329 | version = "0.4.6" 330 | description = "Cross-platform colored terminal text." 331 | optional = false 332 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 333 | files = [ 334 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 335 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 336 | ] 337 | 338 | [[package]] 339 | name = "discord-py" 340 | version = "2.3.2" 341 | description = "A Python wrapper for the Discord API" 342 | optional = false 343 | python-versions = ">=3.8.0" 344 | files = [ 345 | {file = "discord.py-2.3.2-py3-none-any.whl", hash = "sha256:9da4679fc3cb10c64b388284700dc998663e0e57328283bbfcfc2525ec5960a6"}, 346 | {file = "discord.py-2.3.2.tar.gz", hash = "sha256:4560f70f2eddba7e83370ecebd237ac09fbb4980dc66507482b0c0e5b8f76b9c"}, 347 | ] 348 | 349 | [package.dependencies] 350 | aiohttp = ">=3.7.4,<4" 351 | 352 | [package.extras] 353 | docs = ["sphinx (==4.4.0)", "sphinxcontrib-trio (==1.1.2)", "sphinxcontrib-websupport", "typing-extensions (>=4.3,<5)"] 354 | speed = ["Brotli", "aiodns (>=1.1)", "cchardet (==2.1.7)", "orjson (>=3.5.4)"] 355 | test = ["coverage[toml]", "pytest", "pytest-asyncio", "pytest-cov", "pytest-mock", "typing-extensions (>=4.3,<5)"] 356 | voice = ["PyNaCl (>=1.3.0,<1.6)"] 357 | 358 | [[package]] 359 | name = "docutils" 360 | version = "0.20.1" 361 | description = "Docutils -- Python Documentation Utilities" 362 | optional = false 363 | python-versions = ">=3.7" 364 | files = [ 365 | {file = "docutils-0.20.1-py3-none-any.whl", hash = "sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6"}, 366 | {file = "docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"}, 367 | ] 368 | 369 | [[package]] 370 | name = "frozenlist" 371 | version = "1.4.1" 372 | description = "A list-like structure which implements collections.abc.MutableSequence" 373 | optional = false 374 | python-versions = ">=3.8" 375 | files = [ 376 | {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac"}, 377 | {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868"}, 378 | {file = "frozenlist-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776"}, 379 | {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a"}, 380 | {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad"}, 381 | {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c"}, 382 | {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe"}, 383 | {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a"}, 384 | {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98"}, 385 | {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75"}, 386 | {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5"}, 387 | {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950"}, 388 | {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc"}, 389 | {file = "frozenlist-1.4.1-cp310-cp310-win32.whl", hash = "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1"}, 390 | {file = "frozenlist-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439"}, 391 | {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0"}, 392 | {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49"}, 393 | {file = "frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced"}, 394 | {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0"}, 395 | {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106"}, 396 | {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068"}, 397 | {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2"}, 398 | {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19"}, 399 | {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82"}, 400 | {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec"}, 401 | {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a"}, 402 | {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74"}, 403 | {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2"}, 404 | {file = "frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17"}, 405 | {file = "frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825"}, 406 | {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae"}, 407 | {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb"}, 408 | {file = "frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b"}, 409 | {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86"}, 410 | {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480"}, 411 | {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09"}, 412 | {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a"}, 413 | {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd"}, 414 | {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6"}, 415 | {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1"}, 416 | {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b"}, 417 | {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e"}, 418 | {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8"}, 419 | {file = "frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89"}, 420 | {file = "frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5"}, 421 | {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d"}, 422 | {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826"}, 423 | {file = "frozenlist-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb"}, 424 | {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6"}, 425 | {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d"}, 426 | {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887"}, 427 | {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a"}, 428 | {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b"}, 429 | {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701"}, 430 | {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0"}, 431 | {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11"}, 432 | {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09"}, 433 | {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7"}, 434 | {file = "frozenlist-1.4.1-cp38-cp38-win32.whl", hash = "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497"}, 435 | {file = "frozenlist-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09"}, 436 | {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e"}, 437 | {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d"}, 438 | {file = "frozenlist-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8"}, 439 | {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0"}, 440 | {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b"}, 441 | {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0"}, 442 | {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897"}, 443 | {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7"}, 444 | {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742"}, 445 | {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea"}, 446 | {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5"}, 447 | {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9"}, 448 | {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6"}, 449 | {file = "frozenlist-1.4.1-cp39-cp39-win32.whl", hash = "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932"}, 450 | {file = "frozenlist-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0"}, 451 | {file = "frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7"}, 452 | {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}, 453 | ] 454 | 455 | [[package]] 456 | name = "furo" 457 | version = "2023.9.10" 458 | description = "A clean customisable Sphinx documentation theme." 459 | optional = false 460 | python-versions = ">=3.8" 461 | files = [ 462 | {file = "furo-2023.9.10-py3-none-any.whl", hash = "sha256:513092538537dc5c596691da06e3c370714ec99bc438680edc1debffb73e5bfc"}, 463 | {file = "furo-2023.9.10.tar.gz", hash = "sha256:5707530a476d2a63b8cad83b4f961f3739a69f4b058bcf38a03a39fa537195b2"}, 464 | ] 465 | 466 | [package.dependencies] 467 | beautifulsoup4 = "*" 468 | pygments = ">=2.7" 469 | sphinx = ">=6.0,<8.0" 470 | sphinx-basic-ng = "*" 471 | 472 | [[package]] 473 | name = "idna" 474 | version = "3.6" 475 | description = "Internationalized Domain Names in Applications (IDNA)" 476 | optional = false 477 | python-versions = ">=3.5" 478 | files = [ 479 | {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, 480 | {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, 481 | ] 482 | 483 | [[package]] 484 | name = "imagesize" 485 | version = "1.4.1" 486 | description = "Getting image size from png/jpeg/jpeg2000/gif file" 487 | optional = false 488 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 489 | files = [ 490 | {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, 491 | {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, 492 | ] 493 | 494 | [[package]] 495 | name = "import-expression" 496 | version = "1.1.4" 497 | description = "Parses a superset of Python allowing for inline module import expressions" 498 | optional = false 499 | python-versions = "*" 500 | files = [ 501 | {file = "import_expression-1.1.4-py3-none-any.whl", hash = "sha256:292099910a4dcc65ba562377cd2265487ba573dd63d73bdee5deec36ca49555b"}, 502 | {file = "import_expression-1.1.4.tar.gz", hash = "sha256:06086a6ab3bfa528b1c478e633d6adf2b3a990e31440f6401b0f3ea12b0659a9"}, 503 | ] 504 | 505 | [package.dependencies] 506 | astunparse = ">=1.6.3,<2.0.0" 507 | 508 | [package.extras] 509 | test = ["pytest", "pytest-cov"] 510 | 511 | [[package]] 512 | name = "jinja2" 513 | version = "3.1.3" 514 | description = "A very fast and expressive template engine." 515 | optional = false 516 | python-versions = ">=3.7" 517 | files = [ 518 | {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, 519 | {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, 520 | ] 521 | 522 | [package.dependencies] 523 | MarkupSafe = ">=2.0" 524 | 525 | [package.extras] 526 | i18n = ["Babel (>=2.7)"] 527 | 528 | [[package]] 529 | name = "jishaku" 530 | version = "2.5.2a486+g247537e.master" 531 | description = "A discord.py extension including useful tools for bot development and debugging." 532 | optional = false 533 | python-versions = ">=3.10.0" 534 | files = [] 535 | develop = false 536 | 537 | [package.dependencies] 538 | braceexpand = ">=0.1.7" 539 | click = ">=8.1.3" 540 | import_expression = ">=1.0.0,<2.0.0" 541 | tabulate = ">=0.9.0" 542 | 543 | [package.extras] 544 | discordpy = ["discord.py (>=2.3.1)"] 545 | docs = ["Sphinx (>=4.4.0)", "sphinxcontrib_trio (>=1.1.2)"] 546 | procinfo = ["psutil (>=5.9.5)"] 547 | profiling = ["line-profiler (>=4.0.3)"] 548 | publish = ["Jinja2 (>=3.1.2)"] 549 | test = ["coverage (>=7.2.7)", "flake8 (>=6.0.0)", "isort (>=5.12.0)", "pylint (>=2.17.4)", "pytest (>=7.4.0)", "pytest-asyncio (>=0.21.0)", "pytest-cov (>=4.1.0)", "pytest-mock (>=3.11.1)"] 550 | voice = ["yt-dlp (>=2023.6.22)"] 551 | 552 | [package.source] 553 | type = "git" 554 | url = "https://github.com/Gorialis/jishaku/" 555 | reference = "HEAD" 556 | resolved_reference = "247537e14ecff373fe2c3ac7beb1f7dddf5d49d2" 557 | 558 | [[package]] 559 | name = "markupsafe" 560 | version = "2.1.4" 561 | description = "Safely add untrusted strings to HTML/XML markup." 562 | optional = false 563 | python-versions = ">=3.7" 564 | files = [ 565 | {file = "MarkupSafe-2.1.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de8153a7aae3835484ac168a9a9bdaa0c5eee4e0bc595503c95d53b942879c84"}, 566 | {file = "MarkupSafe-2.1.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e888ff76ceb39601c59e219f281466c6d7e66bd375b4ec1ce83bcdc68306796b"}, 567 | {file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0b838c37ba596fcbfca71651a104a611543077156cb0a26fe0c475e1f152ee8"}, 568 | {file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac1ebf6983148b45b5fa48593950f90ed6d1d26300604f321c74a9ca1609f8e"}, 569 | {file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0fbad3d346df8f9d72622ac71b69565e621ada2ce6572f37c2eae8dacd60385d"}, 570 | {file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5291d98cd3ad9a562883468c690a2a238c4a6388ab3bd155b0c75dd55ece858"}, 571 | {file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a7cc49ef48a3c7a0005a949f3c04f8baa5409d3f663a1b36f0eba9bfe2a0396e"}, 572 | {file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b83041cda633871572f0d3c41dddd5582ad7d22f65a72eacd8d3d6d00291df26"}, 573 | {file = "MarkupSafe-2.1.4-cp310-cp310-win32.whl", hash = "sha256:0c26f67b3fe27302d3a412b85ef696792c4a2386293c53ba683a89562f9399b0"}, 574 | {file = "MarkupSafe-2.1.4-cp310-cp310-win_amd64.whl", hash = "sha256:a76055d5cb1c23485d7ddae533229039b850db711c554a12ea64a0fd8a0129e2"}, 575 | {file = "MarkupSafe-2.1.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9e9e3c4020aa2dc62d5dd6743a69e399ce3de58320522948af6140ac959ab863"}, 576 | {file = "MarkupSafe-2.1.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0042d6a9880b38e1dd9ff83146cc3c9c18a059b9360ceae207805567aacccc69"}, 577 | {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55d03fea4c4e9fd0ad75dc2e7e2b6757b80c152c032ea1d1de487461d8140efc"}, 578 | {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ab3a886a237f6e9c9f4f7d272067e712cdb4efa774bef494dccad08f39d8ae6"}, 579 | {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abf5ebbec056817057bfafc0445916bb688a255a5146f900445d081db08cbabb"}, 580 | {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e1a0d1924a5013d4f294087e00024ad25668234569289650929ab871231668e7"}, 581 | {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e7902211afd0af05fbadcc9a312e4cf10f27b779cf1323e78d52377ae4b72bea"}, 582 | {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c669391319973e49a7c6230c218a1e3044710bc1ce4c8e6eb71f7e6d43a2c131"}, 583 | {file = "MarkupSafe-2.1.4-cp311-cp311-win32.whl", hash = "sha256:31f57d64c336b8ccb1966d156932f3daa4fee74176b0fdc48ef580be774aae74"}, 584 | {file = "MarkupSafe-2.1.4-cp311-cp311-win_amd64.whl", hash = "sha256:54a7e1380dfece8847c71bf7e33da5d084e9b889c75eca19100ef98027bd9f56"}, 585 | {file = "MarkupSafe-2.1.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a76cd37d229fc385738bd1ce4cba2a121cf26b53864c1772694ad0ad348e509e"}, 586 | {file = "MarkupSafe-2.1.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:987d13fe1d23e12a66ca2073b8d2e2a75cec2ecb8eab43ff5624ba0ad42764bc"}, 587 | {file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5244324676254697fe5c181fc762284e2c5fceeb1c4e3e7f6aca2b6f107e60dc"}, 588 | {file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78bc995e004681246e85e28e068111a4c3f35f34e6c62da1471e844ee1446250"}, 589 | {file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4d176cfdfde84f732c4a53109b293d05883e952bbba68b857ae446fa3119b4f"}, 590 | {file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f9917691f410a2e0897d1ef99619fd3f7dd503647c8ff2475bf90c3cf222ad74"}, 591 | {file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f06e5a9e99b7df44640767842f414ed5d7bedaaa78cd817ce04bbd6fd86e2dd6"}, 592 | {file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:396549cea79e8ca4ba65525470d534e8a41070e6b3500ce2414921099cb73e8d"}, 593 | {file = "MarkupSafe-2.1.4-cp312-cp312-win32.whl", hash = "sha256:f6be2d708a9d0e9b0054856f07ac7070fbe1754be40ca8525d5adccdbda8f475"}, 594 | {file = "MarkupSafe-2.1.4-cp312-cp312-win_amd64.whl", hash = "sha256:5045e892cfdaecc5b4c01822f353cf2c8feb88a6ec1c0adef2a2e705eef0f656"}, 595 | {file = "MarkupSafe-2.1.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7a07f40ef8f0fbc5ef1000d0c78771f4d5ca03b4953fc162749772916b298fc4"}, 596 | {file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d18b66fe626ac412d96c2ab536306c736c66cf2a31c243a45025156cc190dc8a"}, 597 | {file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:698e84142f3f884114ea8cf83e7a67ca8f4ace8454e78fe960646c6c91c63bfa"}, 598 | {file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49a3b78a5af63ec10d8604180380c13dcd870aba7928c1fe04e881d5c792dc4e"}, 599 | {file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:15866d7f2dc60cfdde12ebb4e75e41be862348b4728300c36cdf405e258415ec"}, 600 | {file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:6aa5e2e7fc9bc042ae82d8b79d795b9a62bd8f15ba1e7594e3db243f158b5565"}, 601 | {file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:54635102ba3cf5da26eb6f96c4b8c53af8a9c0d97b64bdcb592596a6255d8518"}, 602 | {file = "MarkupSafe-2.1.4-cp37-cp37m-win32.whl", hash = "sha256:3583a3a3ab7958e354dc1d25be74aee6228938312ee875a22330c4dc2e41beb0"}, 603 | {file = "MarkupSafe-2.1.4-cp37-cp37m-win_amd64.whl", hash = "sha256:d6e427c7378c7f1b2bef6a344c925b8b63623d3321c09a237b7cc0e77dd98ceb"}, 604 | {file = "MarkupSafe-2.1.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:bf1196dcc239e608605b716e7b166eb5faf4bc192f8a44b81e85251e62584bd2"}, 605 | {file = "MarkupSafe-2.1.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4df98d4a9cd6a88d6a585852f56f2155c9cdb6aec78361a19f938810aa020954"}, 606 | {file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b835aba863195269ea358cecc21b400276747cc977492319fd7682b8cd2c253d"}, 607 | {file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23984d1bdae01bee794267424af55eef4dfc038dc5d1272860669b2aa025c9e3"}, 608 | {file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c98c33ffe20e9a489145d97070a435ea0679fddaabcafe19982fe9c971987d5"}, 609 | {file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9896fca4a8eb246defc8b2a7ac77ef7553b638e04fbf170bff78a40fa8a91474"}, 610 | {file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b0fe73bac2fed83839dbdbe6da84ae2a31c11cfc1c777a40dbd8ac8a6ed1560f"}, 611 | {file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c7556bafeaa0a50e2fe7dc86e0382dea349ebcad8f010d5a7dc6ba568eaaa789"}, 612 | {file = "MarkupSafe-2.1.4-cp38-cp38-win32.whl", hash = "sha256:fc1a75aa8f11b87910ffd98de62b29d6520b6d6e8a3de69a70ca34dea85d2a8a"}, 613 | {file = "MarkupSafe-2.1.4-cp38-cp38-win_amd64.whl", hash = "sha256:3a66c36a3864df95e4f62f9167c734b3b1192cb0851b43d7cc08040c074c6279"}, 614 | {file = "MarkupSafe-2.1.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:765f036a3d00395a326df2835d8f86b637dbaf9832f90f5d196c3b8a7a5080cb"}, 615 | {file = "MarkupSafe-2.1.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:21e7af8091007bf4bebf4521184f4880a6acab8df0df52ef9e513d8e5db23411"}, 616 | {file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5c31fe855c77cad679b302aabc42d724ed87c043b1432d457f4976add1c2c3e"}, 617 | {file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7653fa39578957bc42e5ebc15cf4361d9e0ee4b702d7d5ec96cdac860953c5b4"}, 618 | {file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47bb5f0142b8b64ed1399b6b60f700a580335c8e1c57f2f15587bd072012decc"}, 619 | {file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:fe8512ed897d5daf089e5bd010c3dc03bb1bdae00b35588c49b98268d4a01e00"}, 620 | {file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:36d7626a8cca4d34216875aee5a1d3d654bb3dac201c1c003d182283e3205949"}, 621 | {file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b6f14a9cd50c3cb100eb94b3273131c80d102e19bb20253ac7bd7336118a673a"}, 622 | {file = "MarkupSafe-2.1.4-cp39-cp39-win32.whl", hash = "sha256:c8f253a84dbd2c63c19590fa86a032ef3d8cc18923b8049d91bcdeeb2581fbf6"}, 623 | {file = "MarkupSafe-2.1.4-cp39-cp39-win_amd64.whl", hash = "sha256:8b570a1537367b52396e53325769608f2a687ec9a4363647af1cded8928af959"}, 624 | {file = "MarkupSafe-2.1.4.tar.gz", hash = "sha256:3aae9af4cac263007fd6309c64c6ab4506dd2b79382d9d19a1994f9240b8db4f"}, 625 | ] 626 | 627 | [[package]] 628 | name = "multidict" 629 | version = "6.0.4" 630 | description = "multidict implementation" 631 | optional = false 632 | python-versions = ">=3.7" 633 | files = [ 634 | {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b1a97283e0c85772d613878028fec909f003993e1007eafa715b24b377cb9b8"}, 635 | {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eeb6dcc05e911516ae3d1f207d4b0520d07f54484c49dfc294d6e7d63b734171"}, 636 | {file = "multidict-6.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d6d635d5209b82a3492508cf5b365f3446afb65ae7ebd755e70e18f287b0adf7"}, 637 | {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c048099e4c9e9d615545e2001d3d8a4380bd403e1a0578734e0d31703d1b0c0b"}, 638 | {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea20853c6dbbb53ed34cb4d080382169b6f4554d394015f1bef35e881bf83547"}, 639 | {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16d232d4e5396c2efbbf4f6d4df89bfa905eb0d4dc5b3549d872ab898451f569"}, 640 | {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36c63aaa167f6c6b04ef2c85704e93af16c11d20de1d133e39de6a0e84582a93"}, 641 | {file = "multidict-6.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:64bdf1086b6043bf519869678f5f2757f473dee970d7abf6da91ec00acb9cb98"}, 642 | {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:43644e38f42e3af682690876cff722d301ac585c5b9e1eacc013b7a3f7b696a0"}, 643 | {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7582a1d1030e15422262de9f58711774e02fa80df0d1578995c76214f6954988"}, 644 | {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ddff9c4e225a63a5afab9dd15590432c22e8057e1a9a13d28ed128ecf047bbdc"}, 645 | {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ee2a1ece51b9b9e7752e742cfb661d2a29e7bcdba2d27e66e28a99f1890e4fa0"}, 646 | {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a2e4369eb3d47d2034032a26c7a80fcb21a2cb22e1173d761a162f11e562caa5"}, 647 | {file = "multidict-6.0.4-cp310-cp310-win32.whl", hash = "sha256:574b7eae1ab267e5f8285f0fe881f17efe4b98c39a40858247720935b893bba8"}, 648 | {file = "multidict-6.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:4dcbb0906e38440fa3e325df2359ac6cb043df8e58c965bb45f4e406ecb162cc"}, 649 | {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0dfad7a5a1e39c53ed00d2dd0c2e36aed4650936dc18fd9a1826a5ae1cad6f03"}, 650 | {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:64da238a09d6039e3bd39bb3aee9c21a5e34f28bfa5aa22518581f910ff94af3"}, 651 | {file = "multidict-6.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ff959bee35038c4624250473988b24f846cbeb2c6639de3602c073f10410ceba"}, 652 | {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01a3a55bd90018c9c080fbb0b9f4891db37d148a0a18722b42f94694f8b6d4c9"}, 653 | {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5cb09abb18c1ea940fb99360ea0396f34d46566f157122c92dfa069d3e0e982"}, 654 | {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666daae833559deb2d609afa4490b85830ab0dfca811a98b70a205621a6109fe"}, 655 | {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11bdf3f5e1518b24530b8241529d2050014c884cf18b6fc69c0c2b30ca248710"}, 656 | {file = "multidict-6.0.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d18748f2d30f94f498e852c67d61261c643b349b9d2a581131725595c45ec6c"}, 657 | {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:458f37be2d9e4c95e2d8866a851663cbc76e865b78395090786f6cd9b3bbf4f4"}, 658 | {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b1a2eeedcead3a41694130495593a559a668f382eee0727352b9a41e1c45759a"}, 659 | {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7d6ae9d593ef8641544d6263c7fa6408cc90370c8cb2bbb65f8d43e5b0351d9c"}, 660 | {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5979b5632c3e3534e42ca6ff856bb24b2e3071b37861c2c727ce220d80eee9ed"}, 661 | {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dcfe792765fab89c365123c81046ad4103fcabbc4f56d1c1997e6715e8015461"}, 662 | {file = "multidict-6.0.4-cp311-cp311-win32.whl", hash = "sha256:3601a3cece3819534b11d4efc1eb76047488fddd0c85a3948099d5da4d504636"}, 663 | {file = "multidict-6.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:81a4f0b34bd92df3da93315c6a59034df95866014ac08535fc819f043bfd51f0"}, 664 | {file = "multidict-6.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:67040058f37a2a51ed8ea8f6b0e6ee5bd78ca67f169ce6122f3e2ec80dfe9b78"}, 665 | {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:853888594621e6604c978ce2a0444a1e6e70c8d253ab65ba11657659dcc9100f"}, 666 | {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:39ff62e7d0f26c248b15e364517a72932a611a9b75f35b45be078d81bdb86603"}, 667 | {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af048912e045a2dc732847d33821a9d84ba553f5c5f028adbd364dd4765092ac"}, 668 | {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e8b901e607795ec06c9e42530788c45ac21ef3aaa11dbd0c69de543bfb79a9"}, 669 | {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62501642008a8b9871ddfccbf83e4222cf8ac0d5aeedf73da36153ef2ec222d2"}, 670 | {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:99b76c052e9f1bc0721f7541e5e8c05db3941eb9ebe7b8553c625ef88d6eefde"}, 671 | {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:509eac6cf09c794aa27bcacfd4d62c885cce62bef7b2c3e8b2e49d365b5003fe"}, 672 | {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:21a12c4eb6ddc9952c415f24eef97e3e55ba3af61f67c7bc388dcdec1404a067"}, 673 | {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:5cad9430ab3e2e4fa4a2ef4450f548768400a2ac635841bc2a56a2052cdbeb87"}, 674 | {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ab55edc2e84460694295f401215f4a58597f8f7c9466faec545093045476327d"}, 675 | {file = "multidict-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:5a4dcf02b908c3b8b17a45fb0f15b695bf117a67b76b7ad18b73cf8e92608775"}, 676 | {file = "multidict-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6ed5f161328b7df384d71b07317f4d8656434e34591f20552c7bcef27b0ab88e"}, 677 | {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5fc1b16f586f049820c5c5b17bb4ee7583092fa0d1c4e28b5239181ff9532e0c"}, 678 | {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1502e24330eb681bdaa3eb70d6358e818e8e8f908a22a1851dfd4e15bc2f8161"}, 679 | {file = "multidict-6.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b692f419760c0e65d060959df05f2a531945af31fda0c8a3b3195d4efd06de11"}, 680 | {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45e1ecb0379bfaab5eef059f50115b54571acfbe422a14f668fc8c27ba410e7e"}, 681 | {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddd3915998d93fbcd2566ddf9cf62cdb35c9e093075f862935573d265cf8f65d"}, 682 | {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:59d43b61c59d82f2effb39a93c48b845efe23a3852d201ed2d24ba830d0b4cf2"}, 683 | {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc8e1d0c705233c5dd0c5e6460fbad7827d5d36f310a0fadfd45cc3029762258"}, 684 | {file = "multidict-6.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6aa0418fcc838522256761b3415822626f866758ee0bc6632c9486b179d0b52"}, 685 | {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6748717bb10339c4760c1e63da040f5f29f5ed6e59d76daee30305894069a660"}, 686 | {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4d1a3d7ef5e96b1c9e92f973e43aa5e5b96c659c9bc3124acbbd81b0b9c8a951"}, 687 | {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4372381634485bec7e46718edc71528024fcdc6f835baefe517b34a33c731d60"}, 688 | {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:fc35cb4676846ef752816d5be2193a1e8367b4c1397b74a565a9d0389c433a1d"}, 689 | {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b9d9e4e2b37daddb5c23ea33a3417901fa7c7b3dee2d855f63ee67a0b21e5b1"}, 690 | {file = "multidict-6.0.4-cp38-cp38-win32.whl", hash = "sha256:e41b7e2b59679edfa309e8db64fdf22399eec4b0b24694e1b2104fb789207779"}, 691 | {file = "multidict-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:d6c254ba6e45d8e72739281ebc46ea5eb5f101234f3ce171f0e9f5cc86991480"}, 692 | {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:16ab77bbeb596e14212e7bab8429f24c1579234a3a462105cda4a66904998664"}, 693 | {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc779e9e6f7fda81b3f9aa58e3a6091d49ad528b11ed19f6621408806204ad35"}, 694 | {file = "multidict-6.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ceef517eca3e03c1cceb22030a3e39cb399ac86bff4e426d4fc6ae49052cc60"}, 695 | {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:281af09f488903fde97923c7744bb001a9b23b039a909460d0f14edc7bf59706"}, 696 | {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52f2dffc8acaba9a2f27174c41c9e57f60b907bb9f096b36b1a1f3be71c6284d"}, 697 | {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b41156839806aecb3641f3208c0dafd3ac7775b9c4c422d82ee2a45c34ba81ca"}, 698 | {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5e3fc56f88cc98ef8139255cf8cd63eb2c586531e43310ff859d6bb3a6b51f1"}, 699 | {file = "multidict-6.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8316a77808c501004802f9beebde51c9f857054a0c871bd6da8280e718444449"}, 700 | {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f70b98cd94886b49d91170ef23ec5c0e8ebb6f242d734ed7ed677b24d50c82cf"}, 701 | {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bf6774e60d67a9efe02b3616fee22441d86fab4c6d335f9d2051d19d90a40063"}, 702 | {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:e69924bfcdda39b722ef4d9aa762b2dd38e4632b3641b1d9a57ca9cd18f2f83a"}, 703 | {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:6b181d8c23da913d4ff585afd1155a0e1194c0b50c54fcfe286f70cdaf2b7176"}, 704 | {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52509b5be062d9eafc8170e53026fbc54cf3b32759a23d07fd935fb04fc22d95"}, 705 | {file = "multidict-6.0.4-cp39-cp39-win32.whl", hash = "sha256:27c523fbfbdfd19c6867af7346332b62b586eed663887392cff78d614f9ec313"}, 706 | {file = "multidict-6.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:33029f5734336aa0d4c0384525da0387ef89148dc7191aae00ca5fb23d7aafc2"}, 707 | {file = "multidict-6.0.4.tar.gz", hash = "sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49"}, 708 | ] 709 | 710 | [[package]] 711 | name = "packaging" 712 | version = "23.2" 713 | description = "Core utilities for Python packages" 714 | optional = false 715 | python-versions = ">=3.7" 716 | files = [ 717 | {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, 718 | {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, 719 | ] 720 | 721 | [[package]] 722 | name = "pygments" 723 | version = "2.17.2" 724 | description = "Pygments is a syntax highlighting package written in Python." 725 | optional = false 726 | python-versions = ">=3.7" 727 | files = [ 728 | {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, 729 | {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, 730 | ] 731 | 732 | [package.extras] 733 | plugins = ["importlib-metadata"] 734 | windows-terminal = ["colorama (>=0.4.6)"] 735 | 736 | [[package]] 737 | name = "requests" 738 | version = "2.31.0" 739 | description = "Python HTTP for Humans." 740 | optional = false 741 | python-versions = ">=3.7" 742 | files = [ 743 | {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, 744 | {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, 745 | ] 746 | 747 | [package.dependencies] 748 | certifi = ">=2017.4.17" 749 | charset-normalizer = ">=2,<4" 750 | idna = ">=2.5,<4" 751 | urllib3 = ">=1.21.1,<3" 752 | 753 | [package.extras] 754 | socks = ["PySocks (>=1.5.6,!=1.5.7)"] 755 | use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] 756 | 757 | [[package]] 758 | name = "six" 759 | version = "1.16.0" 760 | description = "Python 2 and 3 compatibility utilities" 761 | optional = false 762 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" 763 | files = [ 764 | {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, 765 | {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, 766 | ] 767 | 768 | [[package]] 769 | name = "snowballstemmer" 770 | version = "2.2.0" 771 | description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." 772 | optional = false 773 | python-versions = "*" 774 | files = [ 775 | {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, 776 | {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, 777 | ] 778 | 779 | [[package]] 780 | name = "soupsieve" 781 | version = "2.5" 782 | description = "A modern CSS selector implementation for Beautiful Soup." 783 | optional = false 784 | python-versions = ">=3.8" 785 | files = [ 786 | {file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"}, 787 | {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"}, 788 | ] 789 | 790 | [[package]] 791 | name = "sphinx" 792 | version = "7.2.6" 793 | description = "Python documentation generator" 794 | optional = false 795 | python-versions = ">=3.9" 796 | files = [ 797 | {file = "sphinx-7.2.6-py3-none-any.whl", hash = "sha256:1e09160a40b956dc623c910118fa636da93bd3ca0b9876a7b3df90f07d691560"}, 798 | {file = "sphinx-7.2.6.tar.gz", hash = "sha256:9a5160e1ea90688d5963ba09a2dcd8bdd526620edbb65c328728f1b2228d5ab5"}, 799 | ] 800 | 801 | [package.dependencies] 802 | alabaster = ">=0.7,<0.8" 803 | babel = ">=2.9" 804 | colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} 805 | docutils = ">=0.18.1,<0.21" 806 | imagesize = ">=1.3" 807 | Jinja2 = ">=3.0" 808 | packaging = ">=21.0" 809 | Pygments = ">=2.14" 810 | requests = ">=2.25.0" 811 | snowballstemmer = ">=2.0" 812 | sphinxcontrib-applehelp = "*" 813 | sphinxcontrib-devhelp = "*" 814 | sphinxcontrib-htmlhelp = ">=2.0.0" 815 | sphinxcontrib-jsmath = "*" 816 | sphinxcontrib-qthelp = "*" 817 | sphinxcontrib-serializinghtml = ">=1.1.9" 818 | 819 | [package.extras] 820 | docs = ["sphinxcontrib-websupport"] 821 | lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-simplify", "isort", "mypy (>=0.990)", "ruff", "sphinx-lint", "types-requests"] 822 | test = ["cython (>=3.0)", "filelock", "html5lib", "pytest (>=4.6)", "setuptools (>=67.0)"] 823 | 824 | [[package]] 825 | name = "sphinx-basic-ng" 826 | version = "1.0.0b2" 827 | description = "A modern skeleton for Sphinx themes." 828 | optional = false 829 | python-versions = ">=3.7" 830 | files = [ 831 | {file = "sphinx_basic_ng-1.0.0b2-py3-none-any.whl", hash = "sha256:eb09aedbabfb650607e9b4b68c9d240b90b1e1be221d6ad71d61c52e29f7932b"}, 832 | {file = "sphinx_basic_ng-1.0.0b2.tar.gz", hash = "sha256:9ec55a47c90c8c002b5960c57492ec3021f5193cb26cebc2dc4ea226848651c9"}, 833 | ] 834 | 835 | [package.dependencies] 836 | sphinx = ">=4.0" 837 | 838 | [package.extras] 839 | docs = ["furo", "ipython", "myst-parser", "sphinx-copybutton", "sphinx-inline-tabs"] 840 | 841 | [[package]] 842 | name = "sphinx-copybutton" 843 | version = "0.5.2" 844 | description = "Add a copy button to each of your code cells." 845 | optional = false 846 | python-versions = ">=3.7" 847 | files = [ 848 | {file = "sphinx-copybutton-0.5.2.tar.gz", hash = "sha256:4cf17c82fb9646d1bc9ca92ac280813a3b605d8c421225fd9913154103ee1fbd"}, 849 | {file = "sphinx_copybutton-0.5.2-py3-none-any.whl", hash = "sha256:fb543fd386d917746c9a2c50360c7905b605726b9355cd26e9974857afeae06e"}, 850 | ] 851 | 852 | [package.dependencies] 853 | sphinx = ">=1.8" 854 | 855 | [package.extras] 856 | code-style = ["pre-commit (==2.12.1)"] 857 | rtd = ["ipython", "myst-nb", "sphinx", "sphinx-book-theme", "sphinx-examples"] 858 | 859 | [[package]] 860 | name = "sphinx-inline-tabs" 861 | version = "2023.4.21" 862 | description = "Add inline tabbed content to your Sphinx documentation." 863 | optional = false 864 | python-versions = ">=3.8" 865 | files = [ 866 | {file = "sphinx_inline_tabs-2023.4.21-py3-none-any.whl", hash = "sha256:06809ac613f7c48ddd6e2fa588413e3fe92cff2397b56e2ccf0b0218f9ef6a78"}, 867 | {file = "sphinx_inline_tabs-2023.4.21.tar.gz", hash = "sha256:5df2f13f602c158f3f5f6c509e008aeada199a8c76d97ba3aa2822206683bebc"}, 868 | ] 869 | 870 | [package.dependencies] 871 | sphinx = ">=3" 872 | 873 | [package.extras] 874 | doc = ["furo", "myst-parser"] 875 | test = ["pytest", "pytest-cov", "pytest-xdist"] 876 | 877 | [[package]] 878 | name = "sphinxcontrib-applehelp" 879 | version = "1.0.8" 880 | description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" 881 | optional = false 882 | python-versions = ">=3.9" 883 | files = [ 884 | {file = "sphinxcontrib_applehelp-1.0.8-py3-none-any.whl", hash = "sha256:cb61eb0ec1b61f349e5cc36b2028e9e7ca765be05e49641c97241274753067b4"}, 885 | {file = "sphinxcontrib_applehelp-1.0.8.tar.gz", hash = "sha256:c40a4f96f3776c4393d933412053962fac2b84f4c99a7982ba42e09576a70619"}, 886 | ] 887 | 888 | [package.extras] 889 | lint = ["docutils-stubs", "flake8", "mypy"] 890 | standalone = ["Sphinx (>=5)"] 891 | test = ["pytest"] 892 | 893 | [[package]] 894 | name = "sphinxcontrib-devhelp" 895 | version = "1.0.6" 896 | description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents" 897 | optional = false 898 | python-versions = ">=3.9" 899 | files = [ 900 | {file = "sphinxcontrib_devhelp-1.0.6-py3-none-any.whl", hash = "sha256:6485d09629944511c893fa11355bda18b742b83a2b181f9a009f7e500595c90f"}, 901 | {file = "sphinxcontrib_devhelp-1.0.6.tar.gz", hash = "sha256:9893fd3f90506bc4b97bdb977ceb8fbd823989f4316b28c3841ec128544372d3"}, 902 | ] 903 | 904 | [package.extras] 905 | lint = ["docutils-stubs", "flake8", "mypy"] 906 | standalone = ["Sphinx (>=5)"] 907 | test = ["pytest"] 908 | 909 | [[package]] 910 | name = "sphinxcontrib-htmlhelp" 911 | version = "2.0.5" 912 | description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" 913 | optional = false 914 | python-versions = ">=3.9" 915 | files = [ 916 | {file = "sphinxcontrib_htmlhelp-2.0.5-py3-none-any.whl", hash = "sha256:393f04f112b4d2f53d93448d4bce35842f62b307ccdc549ec1585e950bc35e04"}, 917 | {file = "sphinxcontrib_htmlhelp-2.0.5.tar.gz", hash = "sha256:0dc87637d5de53dd5eec3a6a01753b1ccf99494bd756aafecd74b4fa9e729015"}, 918 | ] 919 | 920 | [package.extras] 921 | lint = ["docutils-stubs", "flake8", "mypy"] 922 | standalone = ["Sphinx (>=5)"] 923 | test = ["html5lib", "pytest"] 924 | 925 | [[package]] 926 | name = "sphinxcontrib-jsmath" 927 | version = "1.0.1" 928 | description = "A sphinx extension which renders display math in HTML via JavaScript" 929 | optional = false 930 | python-versions = ">=3.5" 931 | files = [ 932 | {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, 933 | {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, 934 | ] 935 | 936 | [package.extras] 937 | test = ["flake8", "mypy", "pytest"] 938 | 939 | [[package]] 940 | name = "sphinxcontrib-qthelp" 941 | version = "1.0.7" 942 | description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents" 943 | optional = false 944 | python-versions = ">=3.9" 945 | files = [ 946 | {file = "sphinxcontrib_qthelp-1.0.7-py3-none-any.whl", hash = "sha256:e2ae3b5c492d58fcbd73281fbd27e34b8393ec34a073c792642cd8e529288182"}, 947 | {file = "sphinxcontrib_qthelp-1.0.7.tar.gz", hash = "sha256:053dedc38823a80a7209a80860b16b722e9e0209e32fea98c90e4e6624588ed6"}, 948 | ] 949 | 950 | [package.extras] 951 | lint = ["docutils-stubs", "flake8", "mypy"] 952 | standalone = ["Sphinx (>=5)"] 953 | test = ["pytest"] 954 | 955 | [[package]] 956 | name = "sphinxcontrib-serializinghtml" 957 | version = "1.1.10" 958 | description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)" 959 | optional = false 960 | python-versions = ">=3.9" 961 | files = [ 962 | {file = "sphinxcontrib_serializinghtml-1.1.10-py3-none-any.whl", hash = "sha256:326369b8df80a7d2d8d7f99aa5ac577f51ea51556ed974e7716cfd4fca3f6cb7"}, 963 | {file = "sphinxcontrib_serializinghtml-1.1.10.tar.gz", hash = "sha256:93f3f5dc458b91b192fe10c397e324f262cf163d79f3282c158e8436a2c4511f"}, 964 | ] 965 | 966 | [package.extras] 967 | lint = ["docutils-stubs", "flake8", "mypy"] 968 | standalone = ["Sphinx (>=5)"] 969 | test = ["pytest"] 970 | 971 | [[package]] 972 | name = "sphinxcontrib-trio" 973 | version = "1.1.2" 974 | description = "Make Sphinx better at documenting Python functions and methods" 975 | optional = false 976 | python-versions = "*" 977 | files = [ 978 | {file = "sphinxcontrib-trio-1.1.2.tar.gz", hash = "sha256:9f1ba9c1d5965b534e85258d8b677dd94e9b1a9a2e918b85ccd42590596b47c0"}, 979 | {file = "sphinxcontrib_trio-1.1.2-py3-none-any.whl", hash = "sha256:1b849be08a147ef4113e35c191a51c5792613a9a54697b497cd91656d906a232"}, 980 | ] 981 | 982 | [package.dependencies] 983 | sphinx = ">=1.7" 984 | 985 | [[package]] 986 | name = "spoti-py" 987 | version = "0.1.3" 988 | description = "An async wrapper for the Spotify Web API." 989 | optional = false 990 | python-versions = ">=3.10.0,<4.0.0" 991 | files = [ 992 | {file = "spoti.py-0.1.3-py3-none-any.whl", hash = "sha256:59b401658b23353197e1db7a7465e349c3d8c4fe646e98c8e614d860a37f25c9"}, 993 | {file = "spoti.py-0.1.3.tar.gz", hash = "sha256:b7bd56bc1ec49fba5cc0b99fa368c8d354dc71a4f141af4b911eaed917f385e7"}, 994 | ] 995 | 996 | [package.dependencies] 997 | aiohttp = ">=3.8.0,<4.0.0" 998 | 999 | [package.extras] 1000 | docs = ["furo (>=2021.11.23,<2022.0.0)", "sphinx (>=4.3.1,<5.0.0)", "sphinx-copybutton (>=0.4.0,<0.5.0)", "sphinxcontrib-trio (>=1.1.2,<2.0.0)", "sphinxext-opengraph (>=0.5.0,<0.6.0)"] 1001 | speed = ["orjson (>=3.6.4,<4.0.0)"] 1002 | 1003 | [[package]] 1004 | name = "tabulate" 1005 | version = "0.9.0" 1006 | description = "Pretty-print tabular data" 1007 | optional = false 1008 | python-versions = ">=3.7" 1009 | files = [ 1010 | {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, 1011 | {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, 1012 | ] 1013 | 1014 | [package.extras] 1015 | widechars = ["wcwidth"] 1016 | 1017 | [[package]] 1018 | name = "typing-extensions" 1019 | version = "4.9.0" 1020 | description = "Backported and Experimental Type Hints for Python 3.8+" 1021 | optional = false 1022 | python-versions = ">=3.8" 1023 | files = [ 1024 | {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, 1025 | {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, 1026 | ] 1027 | 1028 | [[package]] 1029 | name = "urllib3" 1030 | version = "2.2.0" 1031 | description = "HTTP library with thread-safe connection pooling, file post, and more." 1032 | optional = false 1033 | python-versions = ">=3.8" 1034 | files = [ 1035 | {file = "urllib3-2.2.0-py3-none-any.whl", hash = "sha256:ce3711610ddce217e6d113a2732fafad960a03fd0318c91faa79481e35c11224"}, 1036 | {file = "urllib3-2.2.0.tar.gz", hash = "sha256:051d961ad0c62a94e50ecf1af379c3aba230c66c710493493560c0c223c49f20"}, 1037 | ] 1038 | 1039 | [package.extras] 1040 | brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] 1041 | h2 = ["h2 (>=4,<5)"] 1042 | socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] 1043 | zstd = ["zstandard (>=0.18.0)"] 1044 | 1045 | [[package]] 1046 | name = "wheel" 1047 | version = "0.42.0" 1048 | description = "A built-package format for Python" 1049 | optional = false 1050 | python-versions = ">=3.7" 1051 | files = [ 1052 | {file = "wheel-0.42.0-py3-none-any.whl", hash = "sha256:177f9c9b0d45c47873b619f5b650346d632cdc35fb5e4d25058e09c9e581433d"}, 1053 | {file = "wheel-0.42.0.tar.gz", hash = "sha256:c45be39f7882c9d34243236f2d63cbd58039e360f85d0913425fbd7ceea617a8"}, 1054 | ] 1055 | 1056 | [package.extras] 1057 | test = ["pytest (>=6.0.0)", "setuptools (>=65)"] 1058 | 1059 | [[package]] 1060 | name = "yarl" 1061 | version = "1.9.4" 1062 | description = "Yet another URL library" 1063 | optional = false 1064 | python-versions = ">=3.7" 1065 | files = [ 1066 | {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e"}, 1067 | {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2"}, 1068 | {file = "yarl-1.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66"}, 1069 | {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234"}, 1070 | {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392"}, 1071 | {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551"}, 1072 | {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455"}, 1073 | {file = "yarl-1.9.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c"}, 1074 | {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53"}, 1075 | {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385"}, 1076 | {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863"}, 1077 | {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b"}, 1078 | {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541"}, 1079 | {file = "yarl-1.9.4-cp310-cp310-win32.whl", hash = "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d"}, 1080 | {file = "yarl-1.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b"}, 1081 | {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099"}, 1082 | {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c"}, 1083 | {file = "yarl-1.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0"}, 1084 | {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525"}, 1085 | {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8"}, 1086 | {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9"}, 1087 | {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42"}, 1088 | {file = "yarl-1.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe"}, 1089 | {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce"}, 1090 | {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9"}, 1091 | {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572"}, 1092 | {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958"}, 1093 | {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98"}, 1094 | {file = "yarl-1.9.4-cp311-cp311-win32.whl", hash = "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31"}, 1095 | {file = "yarl-1.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1"}, 1096 | {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81"}, 1097 | {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142"}, 1098 | {file = "yarl-1.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074"}, 1099 | {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129"}, 1100 | {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2"}, 1101 | {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78"}, 1102 | {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4"}, 1103 | {file = "yarl-1.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0"}, 1104 | {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51"}, 1105 | {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff"}, 1106 | {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7"}, 1107 | {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc"}, 1108 | {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10"}, 1109 | {file = "yarl-1.9.4-cp312-cp312-win32.whl", hash = "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7"}, 1110 | {file = "yarl-1.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984"}, 1111 | {file = "yarl-1.9.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f"}, 1112 | {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17"}, 1113 | {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14"}, 1114 | {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5"}, 1115 | {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd"}, 1116 | {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7"}, 1117 | {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e"}, 1118 | {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec"}, 1119 | {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c"}, 1120 | {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead"}, 1121 | {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434"}, 1122 | {file = "yarl-1.9.4-cp37-cp37m-win32.whl", hash = "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749"}, 1123 | {file = "yarl-1.9.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2"}, 1124 | {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be"}, 1125 | {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f"}, 1126 | {file = "yarl-1.9.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf"}, 1127 | {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1"}, 1128 | {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57"}, 1129 | {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa"}, 1130 | {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130"}, 1131 | {file = "yarl-1.9.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559"}, 1132 | {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23"}, 1133 | {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec"}, 1134 | {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78"}, 1135 | {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be"}, 1136 | {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3"}, 1137 | {file = "yarl-1.9.4-cp38-cp38-win32.whl", hash = "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece"}, 1138 | {file = "yarl-1.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b"}, 1139 | {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27"}, 1140 | {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1"}, 1141 | {file = "yarl-1.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91"}, 1142 | {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b"}, 1143 | {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5"}, 1144 | {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34"}, 1145 | {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136"}, 1146 | {file = "yarl-1.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7"}, 1147 | {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e"}, 1148 | {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4"}, 1149 | {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec"}, 1150 | {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c"}, 1151 | {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0"}, 1152 | {file = "yarl-1.9.4-cp39-cp39-win32.whl", hash = "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575"}, 1153 | {file = "yarl-1.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15"}, 1154 | {file = "yarl-1.9.4-py3-none-any.whl", hash = "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad"}, 1155 | {file = "yarl-1.9.4.tar.gz", hash = "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf"}, 1156 | ] 1157 | 1158 | [package.dependencies] 1159 | idna = ">=2.0" 1160 | multidict = ">=4.0" 1161 | 1162 | [metadata] 1163 | lock-version = "2.0" 1164 | python-versions = "^3.12.0" 1165 | content-hash = "10e3f1bf853b951904462a96a0e8c613e3a20bd96b24fea62521e2723b5cf4d7" 1166 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["poetry-core"] 3 | build-backend = "poetry.core.masonry.api" 4 | 5 | [tool.poetry] 6 | name = "discord-ext-lava" 7 | version = "1.0.0a1" 8 | description = "A discord.py extension for the Lavalink API and WebSocket." 9 | license = "MIT" 10 | authors = ["Aaron Hennessey "] 11 | readme = "README.md" 12 | homepage = "https://github.com/aaronhnsy/discord-ext-lava" 13 | repository = "https://github.com/aaronhnsy/discord-ext-lava" 14 | documentation = "https://discord-ext-lava.readthedocs.io/en/latest/" 15 | keywords = [ 16 | "discord", "discord.py", "discord-ext", "lavalink", "lavaplayer", "api", "wrapper", "async" 17 | ] 18 | classifiers = [ 19 | "Development Status :: 3 - Alpha", 20 | "Environment :: Other Environment", 21 | "Framework :: AsyncIO", 22 | "Intended Audience :: Developers", 23 | "Natural Language :: English", 24 | "Operating System :: OS Independent", 25 | "Programming Language :: Python :: Implementation :: CPython", 26 | "Topic :: Software Development", 27 | "Topic :: Software Development :: Libraries", 28 | "Topic :: Software Development :: Libraries :: Python Modules", 29 | "Typing :: Typed", 30 | ] 31 | packages = [ 32 | { include = "discord/ext/lava" }, 33 | { include = "discord/ext/lava/**/*.py" }, 34 | { include = "discord/ext/lava/**/*.typed" }, 35 | ] 36 | include = [ 37 | "CHANGELOG.md", "LICENSE" 38 | ] 39 | 40 | [tool.poetry.dependencies] 41 | python = "^3.12.0" 42 | aiohttp = "^3.9.0" 43 | discord-py = "^2.3.0" 44 | spoti-py = "^0.1.0" 45 | typing-extensions = "^4.8.0" 46 | 47 | [tool.poetry.group.docs] 48 | optional = true 49 | 50 | [tool.poetry.group.docs.dependencies] 51 | sphinx = "^7.2.0" 52 | sphinxcontrib-trio = "^1.1.0" 53 | sphinx-copybutton = "^0.5.0" 54 | sphinx-inline-tabs = "^2023.4.0" 55 | furo = "^2023.9.0" 56 | 57 | [tool.poetry.group.dev] 58 | optional = true 59 | 60 | [tool.poetry.group.dev.dependencies] 61 | colorama = "^0.4.0" 62 | jishaku = { git = "https://github.com/Gorialis/jishaku/" } 63 | 64 | [tool.poetry.urls] 65 | "Issue Tracker" = "https://github.com/aaronhnsy/discord-ext-lava/issues" 66 | 67 | [tool.pyright] 68 | include = ["discord/ext/lava"] 69 | pythonVersion = "3.12" 70 | typeCheckingMode = "strict" 71 | useLibraryCodeForTypes = true 72 | reportPrivateUsage = false 73 | reportUnknownMemberType = false 74 | reportUnnecessaryIsInstance = false 75 | 76 | [tool.isort] 77 | line_length = 110 78 | lines_after_imports = 2 79 | multi_line_output = 9 80 | include_trailing_comma = true 81 | extra_standard_library = ["typing_extensions"] 82 | known_thirdparty = ["discord"] 83 | --------------------------------------------------------------------------------