├── .gitignore ├── config.py ├── keyboards ├── __init__.py ├── common_keyboards.py └── inline_keyboards │ ├── __init__.py │ ├── actions_kb.py │ ├── info_kb.py │ └── shop_kb.py ├── main.py ├── poetry.lock ├── pyproject.toml ├── routers ├── __init__.py ├── admin_handlers.py ├── callback_handlers │ ├── __init__.py │ ├── actions_kb_callback_handlers.py │ ├── info_kb_callback_handlers.py │ └── shop_kb_callback_handlers.py ├── commands │ ├── __init__.py │ ├── base_commands.py │ └── user_commands.py ├── common.py ├── media_handlers.py └── survey │ ├── __init__.py │ ├── handlers.py │ ├── states.py │ └── survey_handlers │ ├── __init__.py │ ├── email_newsletter_handlers.py │ ├── full_name.py │ ├── select_sport_handlers.py │ └── user_email_handlers.py └── validators ├── __init__.py └── email_validators.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/#use-with-ide 110 | .pdm.toml 111 | 112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 113 | __pypackages__/ 114 | 115 | # Celery stuff 116 | celerybeat-schedule 117 | celerybeat.pid 118 | 119 | # SageMath parsed files 120 | *.sage.py 121 | 122 | # Environments 123 | .env 124 | .venv 125 | env/ 126 | venv/ 127 | ENV/ 128 | env.bak/ 129 | venv.bak/ 130 | 131 | # Spyder project settings 132 | .spyderproject 133 | .spyproject 134 | 135 | # Rope project settings 136 | .ropeproject 137 | 138 | # mkdocs documentation 139 | /site 140 | 141 | # mypy 142 | .mypy_cache/ 143 | .dmypy.json 144 | dmypy.json 145 | 146 | # Pyre type checker 147 | .pyre/ 148 | 149 | # pytype static type analyzer 150 | .pytype/ 151 | 152 | # Cython debug symbols 153 | cython_debug/ 154 | 155 | # PyCharm 156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 158 | # and can be added to the global gitignore or merged into this file. For a more nuclear 159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 160 | .idea/ 161 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | from pydantic_settings import BaseSettings, SettingsConfigDict 2 | 3 | 4 | class Settings(BaseSettings): 5 | model_config = SettingsConfigDict( 6 | case_sensitive=False, 7 | ) 8 | 9 | bot_token: str 10 | admin_ids: frozenset[int] = frozenset({42, 3595399}) 11 | 12 | 13 | settings = Settings() 14 | -------------------------------------------------------------------------------- /keyboards/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mahenzon/demo-tg-bot/e4b687d51600722f1f93b26fdc17d329bad06ee1/keyboards/__init__.py -------------------------------------------------------------------------------- /keyboards/common_keyboards.py: -------------------------------------------------------------------------------- 1 | from typing import Iterable 2 | 3 | from aiogram.types import ( 4 | KeyboardButton, 5 | ReplyKeyboardMarkup, 6 | KeyboardButtonPollType, 7 | ) 8 | from aiogram.utils.keyboard import ReplyKeyboardBuilder 9 | 10 | 11 | class ButtonText: 12 | HELLO = "Hello!" 13 | WHATS_NEXT = "What's next?" 14 | BYE = "Bye-bye" 15 | 16 | 17 | def get_on_start_kb() -> ReplyKeyboardMarkup: 18 | button_hello = KeyboardButton(text=ButtonText.HELLO) 19 | button_help = KeyboardButton(text=ButtonText.WHATS_NEXT) 20 | button_bye = KeyboardButton(text=ButtonText.BYE) 21 | buttons_first_row = [button_hello, button_help] 22 | buttons_second_row = [button_bye] 23 | markup = ReplyKeyboardMarkup( 24 | keyboard=[buttons_first_row, buttons_second_row], 25 | resize_keyboard=True, 26 | # one_time_keyboard=True, 27 | ) 28 | return markup 29 | 30 | 31 | def get_on_help_kb() -> ReplyKeyboardMarkup: 32 | numbers = [ 33 | "1️⃣", 34 | "2️⃣", 35 | "3️⃣", 36 | "4️⃣", 37 | "5️⃣", 38 | "6️⃣", 39 | "7️⃣", 40 | "8️⃣", 41 | "9️⃣", 42 | "0️⃣", 43 | ] 44 | buttons_row = [KeyboardButton(text=num) for num in numbers] 45 | # buttons_row.append(buttons_row[0]) 46 | # buttons_row.append(buttons_row[1]) 47 | # # buttons_row.append(buttons_row[2]) 48 | # # buttons_row.pop(0) 49 | # 50 | # markup = ReplyKeyboardMarkup( 51 | # keyboard=[buttons_row, buttons_row], 52 | # resize_keyboard=True, 53 | # ) 54 | # return markup 55 | builder = ReplyKeyboardBuilder() 56 | for num in numbers: 57 | # builder.button(text=num) 58 | builder.add(KeyboardButton(text=num)) 59 | # builder.adjust(3, 3, 4) 60 | builder.adjust(3) 61 | builder.row(buttons_row[3], buttons_row[1]) 62 | builder.add(buttons_row[-1]) 63 | return builder.as_markup(resize_keyboard=False) 64 | 65 | 66 | def get_actions_kb() -> ReplyKeyboardMarkup: 67 | # markup = ReplyKeyboardMarkup( 68 | # input_field_placeholder="" 69 | # # keyboard=[] 70 | # ) 71 | 72 | # return markup 73 | builder = ReplyKeyboardBuilder() 74 | # builder.add(KeyboardButton(text="🌍 Send Location", request_location=True)) 75 | builder.button( 76 | text="🌍 Send Location", 77 | request_location=True, 78 | ) 79 | builder.button( 80 | text="☎️ Send My Phone", 81 | request_contact=True, 82 | ) 83 | builder.button( 84 | text="📊 Send Poll", 85 | request_poll=KeyboardButtonPollType(), 86 | ) 87 | builder.button( 88 | text="👾 Send Quiz", 89 | request_poll=KeyboardButtonPollType(type="quiz"), 90 | ) 91 | builder.button( 92 | text="🍽️ Dinner?", 93 | request_poll=KeyboardButtonPollType(type="regular"), 94 | ) 95 | builder.button(text=ButtonText.BYE) 96 | builder.adjust(1) 97 | return builder.as_markup( 98 | input_field_placeholder="Actions:", 99 | resize_keyboard=True, 100 | ) 101 | 102 | 103 | def build_yes_or_no_keyboard() -> ReplyKeyboardMarkup: 104 | builder = ReplyKeyboardBuilder() 105 | builder.button(text="Yes") 106 | builder.button(text="No") 107 | # builder.adjust(1) 108 | return builder.as_markup(resize_keyboard=True) 109 | 110 | 111 | def build_select_keyboard(options: Iterable[str]) -> ReplyKeyboardMarkup: 112 | builder = ReplyKeyboardBuilder() 113 | for option in options: 114 | builder.button(text=option) 115 | builder.adjust(1) 116 | return builder.as_markup(resize_keyboard=True) 117 | -------------------------------------------------------------------------------- /keyboards/inline_keyboards/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mahenzon/demo-tg-bot/e4b687d51600722f1f93b26fdc17d329bad06ee1/keyboards/inline_keyboards/__init__.py -------------------------------------------------------------------------------- /keyboards/inline_keyboards/actions_kb.py: -------------------------------------------------------------------------------- 1 | from random import randint 2 | 3 | from aiogram.filters.callback_data import CallbackData 4 | from aiogram.types import InlineKeyboardMarkup 5 | from aiogram.utils.keyboard import InlineKeyboardBuilder 6 | 7 | 8 | random_num_updated_cb_data = "random_num_updated_cb_data" 9 | 10 | 11 | class FixedRandomNumCbData(CallbackData, prefix="fixed-random-num"): 12 | number: int 13 | 14 | 15 | def build_actions_kb( 16 | random_number_button_text="Random number", 17 | ) -> InlineKeyboardMarkup: 18 | builder = InlineKeyboardBuilder() 19 | builder.button( 20 | text=random_number_button_text, 21 | callback_data=random_num_updated_cb_data, 22 | ) 23 | cb_data_1 = FixedRandomNumCbData(number=randint(1, 100)) 24 | builder.button( 25 | text=f"Random number: {cb_data_1.number}", 26 | callback_data=cb_data_1.pack(), 27 | ) 28 | builder.button( 29 | text="Random number: [HIDDEN]", 30 | callback_data=FixedRandomNumCbData(number=randint(1, 100)).pack(), 31 | ) 32 | builder.adjust(1) 33 | return builder.as_markup() 34 | -------------------------------------------------------------------------------- /keyboards/inline_keyboards/info_kb.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | from aiogram.filters.callback_data import CallbackData 4 | from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton 5 | 6 | from .actions_kb import random_num_updated_cb_data 7 | 8 | 9 | class RandomNumAction(Enum): 10 | dice = "dice" 11 | modal = "modal" 12 | 13 | 14 | class RandomNumCbData(CallbackData, prefix="random_num"): 15 | action: RandomNumAction 16 | 17 | 18 | def build_info_kb() -> InlineKeyboardMarkup: 19 | tg_channel_btn = InlineKeyboardButton( 20 | text="📣 Канал", 21 | url="https://t.me/Khorenyan", 22 | ) 23 | tg_chat_btn = InlineKeyboardButton( 24 | text="💬 Чат", 25 | url="https://t.me/SurenTalk", 26 | ) 27 | bot_source_code_btn = InlineKeyboardButton( 28 | text="🤖 Исходный код этого бота", 29 | url="https://github.com/mahenzon/demo-tg-bot", 30 | ) 31 | btn_random_site = InlineKeyboardButton( 32 | text="Random number message", 33 | callback_data=random_num_updated_cb_data, 34 | ) 35 | btn_random_num = InlineKeyboardButton( 36 | text="🎲 Random Num", 37 | callback_data=RandomNumCbData(action=RandomNumAction.dice).pack(), 38 | ) 39 | btn_random_num_modal = InlineKeyboardButton( 40 | text="👾 Random Number", 41 | callback_data=RandomNumCbData(action=RandomNumAction.modal).pack(), 42 | ) 43 | row_tg = [tg_channel_btn, tg_chat_btn] 44 | # row_first = [tg_channel_btn] 45 | # row_second = [tg_chat_btn] 46 | rows = [ 47 | # row_first, 48 | # row_second, 49 | row_tg, 50 | [bot_source_code_btn], 51 | [btn_random_site], 52 | [btn_random_num], 53 | [btn_random_num_modal], 54 | ] 55 | markup = InlineKeyboardMarkup(inline_keyboard=rows) 56 | return markup 57 | -------------------------------------------------------------------------------- /keyboards/inline_keyboards/shop_kb.py: -------------------------------------------------------------------------------- 1 | from enum import IntEnum, auto 2 | 3 | from aiogram.filters.callback_data import CallbackData 4 | from aiogram.types import InlineKeyboardMarkup 5 | from aiogram.utils.keyboard import InlineKeyboardBuilder 6 | 7 | 8 | class ShopActions(IntEnum): 9 | products = auto() 10 | address = auto() 11 | root = auto() 12 | 13 | 14 | class ShopCbData(CallbackData, prefix="shop"): 15 | action: ShopActions 16 | 17 | 18 | class ProductActions(IntEnum): 19 | details = auto() 20 | update = auto() 21 | delete = auto() 22 | 23 | 24 | class ProductCbData(CallbackData, prefix="product"): 25 | action: ProductActions 26 | id: int 27 | title: str 28 | price: int 29 | 30 | 31 | def build_shop_kb() -> InlineKeyboardMarkup: 32 | builder = InlineKeyboardBuilder() 33 | builder.button( 34 | text="Show products", 35 | callback_data=ShopCbData(action=ShopActions.products).pack(), 36 | ) 37 | builder.button( 38 | text="My address", 39 | callback_data=ShopCbData(action=ShopActions.address).pack(), 40 | ) 41 | builder.adjust(1) 42 | return builder.as_markup() 43 | 44 | 45 | def build_products_kb() -> InlineKeyboardMarkup: 46 | builder = InlineKeyboardBuilder() 47 | builder.button( 48 | text="Back to root", 49 | callback_data=ShopCbData(action=ShopActions.root).pack(), 50 | ) 51 | for idx, (name, price) in enumerate( 52 | [ 53 | ("Tablet", 999), 54 | ("Laptop", 1299), 55 | ("Desktop", 2499), 56 | ], 57 | start=1, 58 | ): 59 | builder.button( 60 | text=name, 61 | callback_data=ProductCbData( 62 | action=ProductActions.details, 63 | id=idx, 64 | title=name, 65 | price=price, 66 | ), 67 | ) 68 | builder.adjust(1) 69 | return builder.as_markup() 70 | 71 | 72 | def product_details_kb( 73 | product_cb_data: ProductCbData, 74 | ) -> InlineKeyboardMarkup: 75 | builder = InlineKeyboardBuilder() 76 | builder.button( 77 | text="⬅️ Back to products", 78 | callback_data=ShopCbData(action=ShopActions.products).pack(), 79 | ) 80 | for label, action in [ 81 | ("Update", ProductActions.update), 82 | ("Delete", ProductActions.delete), 83 | ]: 84 | builder.button( 85 | text=label, 86 | callback_data=ProductCbData( 87 | action=action, 88 | **product_cb_data.model_dump(include={"id", "title", "price"}), 89 | # **product_cb_data.model_dump(exclude={"action"}), 90 | # id=product_cb_data.id, 91 | # title=product_cb_data.title, 92 | # price=product_cb_data.price, 93 | ), 94 | ) 95 | builder.adjust(1, 2) 96 | return builder.as_markup() 97 | 98 | 99 | def build_update_product_kb( 100 | product_cb_data: ProductCbData, 101 | ) -> InlineKeyboardMarkup: 102 | builder = InlineKeyboardBuilder() 103 | 104 | builder.button( 105 | text=f"⬅️ back to {product_cb_data.title}", 106 | callback_data=ProductCbData( 107 | action=ProductActions.details, 108 | **product_cb_data.model_dump(include={"id", "title", "price"}), 109 | ), 110 | ) 111 | builder.button( 112 | text="🔄 Update", 113 | callback_data="...", 114 | ) 115 | return builder.as_markup() 116 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import logging 3 | 4 | from aiogram import Bot 5 | from aiogram import Dispatcher 6 | 7 | from aiogram.enums import ParseMode 8 | 9 | from config import settings 10 | from routers import router as main_router 11 | 12 | 13 | async def main(): 14 | dp = Dispatcher() 15 | dp.include_router(main_router) 16 | 17 | logging.basicConfig(level=logging.INFO) 18 | bot = Bot( 19 | token=settings.bot_token, 20 | parse_mode=ParseMode.HTML, 21 | ) 22 | await dp.start_polling(bot) 23 | 24 | 25 | if __name__ == "__main__": 26 | asyncio.run(main()) 27 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. 2 | 3 | [[package]] 4 | name = "aiofiles" 5 | version = "23.2.1" 6 | description = "File support for asyncio." 7 | optional = false 8 | python-versions = ">=3.7" 9 | files = [ 10 | {file = "aiofiles-23.2.1-py3-none-any.whl", hash = "sha256:19297512c647d4b27a2cf7c34caa7e405c0d60b5560618a29a9fe027b18b0107"}, 11 | {file = "aiofiles-23.2.1.tar.gz", hash = "sha256:84ec2218d8419404abcb9f0c02df3f34c6e0a68ed41072acfb1cef5cbc29051a"}, 12 | ] 13 | 14 | [[package]] 15 | name = "aiogram" 16 | version = "3.4.1" 17 | description = "Modern and fully asynchronous framework for Telegram Bot API" 18 | optional = false 19 | python-versions = ">=3.8" 20 | files = [ 21 | {file = "aiogram-3.4.1-py3-none-any.whl", hash = "sha256:5e403945d41001a1810aa8f470ed31fb24a8cb2cde7b16363d1fcd20a757ff68"}, 22 | {file = "aiogram-3.4.1.tar.gz", hash = "sha256:32ba2f6b0ca5488eae03e794a24c779234e909e882445394ef691b357d809aa8"}, 23 | ] 24 | 25 | [package.dependencies] 26 | aiofiles = ">=23.2.1,<23.3.0" 27 | aiohttp = ">=3.9.0,<3.10.0" 28 | certifi = ">=2023.7.22" 29 | magic-filter = ">=1.0.12,<1.1" 30 | pydantic = ">=2.4.1,<2.6" 31 | typing-extensions = ">=4.7.0,<=5.0" 32 | 33 | [package.extras] 34 | cli = ["aiogram-cli (>=1.0.3,<1.1.0)"] 35 | dev = ["black (>=23.10.0,<23.11.0)", "isort (>=5.12.0,<5.13.0)", "mypy (>=1.6.1,<1.7.0)", "packaging (>=23.1,<24.0)", "pre-commit (>=3.5.0,<3.6.0)", "ruff (>=0.1.1,<0.2.0)", "toml (>=0.10.2,<0.11.0)"] 36 | docs = ["furo (>=2023.9.10,<2023.10.0)", "markdown-include (>=0.8.1,<0.9.0)", "pygments (>=2.16.1,<2.17.0)", "pymdown-extensions (>=10.3,<11.0)", "sphinx (>=7.2.6,<7.3.0)", "sphinx-autobuild (>=2021.3.14,<2021.4.0)", "sphinx-copybutton (>=0.5.2,<0.6.0)", "sphinx-intl (>=2.1.0,<2.2.0)", "sphinx-substitution-extensions (>=2022.2.16,<2022.3.0)", "sphinxcontrib-towncrier (>=0.3.2a0,<0.4.0)", "towncrier (>=23.6.0,<23.7.0)"] 37 | fast = ["aiodns (>=3.0.0)", "uvloop (>=0.17.0)"] 38 | i18n = ["babel (>=2.13.0,<2.14.0)"] 39 | proxy = ["aiohttp-socks (>=0.8.3,<0.9.0)"] 40 | redis = ["redis[hiredis] (>=5.0.1,<5.1.0)"] 41 | test = ["aresponses (>=2.1.6,<2.2.0)", "pycryptodomex (>=3.19.0,<3.20.0)", "pytest (>=7.4.2,<7.5.0)", "pytest-aiohttp (>=1.0.5,<1.1.0)", "pytest-asyncio (>=0.21.1,<0.22.0)", "pytest-cov (>=4.1.0,<4.2.0)", "pytest-html (>=4.0.2,<4.1.0)", "pytest-lazy-fixture (>=0.6.3,<0.7.0)", "pytest-mock (>=3.12.0,<3.13.0)", "pytest-mypy (>=0.10.3,<0.11.0)", "pytz (>=2023.3,<2024.0)"] 42 | 43 | [[package]] 44 | name = "aiohttp" 45 | version = "3.9.3" 46 | description = "Async http client/server framework (asyncio)" 47 | optional = false 48 | python-versions = ">=3.8" 49 | files = [ 50 | {file = "aiohttp-3.9.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:939677b61f9d72a4fa2a042a5eee2a99a24001a67c13da113b2e30396567db54"}, 51 | {file = "aiohttp-3.9.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1f5cd333fcf7590a18334c90f8c9147c837a6ec8a178e88d90a9b96ea03194cc"}, 52 | {file = "aiohttp-3.9.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:82e6aa28dd46374f72093eda8bcd142f7771ee1eb9d1e223ff0fa7177a96b4a5"}, 53 | {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f56455b0c2c7cc3b0c584815264461d07b177f903a04481dfc33e08a89f0c26b"}, 54 | {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bca77a198bb6e69795ef2f09a5f4c12758487f83f33d63acde5f0d4919815768"}, 55 | {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e083c285857b78ee21a96ba1eb1b5339733c3563f72980728ca2b08b53826ca5"}, 56 | {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab40e6251c3873d86ea9b30a1ac6d7478c09277b32e14745d0d3c6e76e3c7e29"}, 57 | {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df822ee7feaaeffb99c1a9e5e608800bd8eda6e5f18f5cfb0dc7eeb2eaa6bbec"}, 58 | {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:acef0899fea7492145d2bbaaaec7b345c87753168589cc7faf0afec9afe9b747"}, 59 | {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cd73265a9e5ea618014802ab01babf1940cecb90c9762d8b9e7d2cc1e1969ec6"}, 60 | {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:a78ed8a53a1221393d9637c01870248a6f4ea5b214a59a92a36f18151739452c"}, 61 | {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:6b0e029353361f1746bac2e4cc19b32f972ec03f0f943b390c4ab3371840aabf"}, 62 | {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7cf5c9458e1e90e3c390c2639f1017a0379a99a94fdfad3a1fd966a2874bba52"}, 63 | {file = "aiohttp-3.9.3-cp310-cp310-win32.whl", hash = "sha256:3e59c23c52765951b69ec45ddbbc9403a8761ee6f57253250c6e1536cacc758b"}, 64 | {file = "aiohttp-3.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:055ce4f74b82551678291473f66dc9fb9048a50d8324278751926ff0ae7715e5"}, 65 | {file = "aiohttp-3.9.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6b88f9386ff1ad91ace19d2a1c0225896e28815ee09fc6a8932fded8cda97c3d"}, 66 | {file = "aiohttp-3.9.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c46956ed82961e31557b6857a5ca153c67e5476972e5f7190015018760938da2"}, 67 | {file = "aiohttp-3.9.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:07b837ef0d2f252f96009e9b8435ec1fef68ef8b1461933253d318748ec1acdc"}, 68 | {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad46e6f620574b3b4801c68255492e0159d1712271cc99d8bdf35f2043ec266"}, 69 | {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ed3e046ea7b14938112ccd53d91c1539af3e6679b222f9469981e3dac7ba1ce"}, 70 | {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:039df344b45ae0b34ac885ab5b53940b174530d4dd8a14ed8b0e2155b9dddccb"}, 71 | {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7943c414d3a8d9235f5f15c22ace69787c140c80b718dcd57caaade95f7cd93b"}, 72 | {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84871a243359bb42c12728f04d181a389718710129b36b6aad0fc4655a7647d4"}, 73 | {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5eafe2c065df5401ba06821b9a054d9cb2848867f3c59801b5d07a0be3a380ae"}, 74 | {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9d3c9b50f19704552f23b4eaea1fc082fdd82c63429a6506446cbd8737823da3"}, 75 | {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:f033d80bc6283092613882dfe40419c6a6a1527e04fc69350e87a9df02bbc283"}, 76 | {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:2c895a656dd7e061b2fd6bb77d971cc38f2afc277229ce7dd3552de8313a483e"}, 77 | {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1f5a71d25cd8106eab05f8704cd9167b6e5187bcdf8f090a66c6d88b634802b4"}, 78 | {file = "aiohttp-3.9.3-cp311-cp311-win32.whl", hash = "sha256:50fca156d718f8ced687a373f9e140c1bb765ca16e3d6f4fe116e3df7c05b2c5"}, 79 | {file = "aiohttp-3.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:5fe9ce6c09668063b8447f85d43b8d1c4e5d3d7e92c63173e6180b2ac5d46dd8"}, 80 | {file = "aiohttp-3.9.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:38a19bc3b686ad55804ae931012f78f7a534cce165d089a2059f658f6c91fa60"}, 81 | {file = "aiohttp-3.9.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:770d015888c2a598b377bd2f663adfd947d78c0124cfe7b959e1ef39f5b13869"}, 82 | {file = "aiohttp-3.9.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ee43080e75fc92bf36219926c8e6de497f9b247301bbf88c5c7593d931426679"}, 83 | {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52df73f14ed99cee84865b95a3d9e044f226320a87af208f068ecc33e0c35b96"}, 84 | {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc9b311743a78043b26ffaeeb9715dc360335e5517832f5a8e339f8a43581e4d"}, 85 | {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b955ed993491f1a5da7f92e98d5dad3c1e14dc175f74517c4e610b1f2456fb11"}, 86 | {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:504b6981675ace64c28bf4a05a508af5cde526e36492c98916127f5a02354d53"}, 87 | {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a6fe5571784af92b6bc2fda8d1925cccdf24642d49546d3144948a6a1ed58ca5"}, 88 | {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ba39e9c8627edc56544c8628cc180d88605df3892beeb2b94c9bc857774848ca"}, 89 | {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e5e46b578c0e9db71d04c4b506a2121c0cb371dd89af17a0586ff6769d4c58c1"}, 90 | {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:938a9653e1e0c592053f815f7028e41a3062e902095e5a7dc84617c87267ebd5"}, 91 | {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:c3452ea726c76e92f3b9fae4b34a151981a9ec0a4847a627c43d71a15ac32aa6"}, 92 | {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ff30218887e62209942f91ac1be902cc80cddb86bf00fbc6783b7a43b2bea26f"}, 93 | {file = "aiohttp-3.9.3-cp312-cp312-win32.whl", hash = "sha256:38f307b41e0bea3294a9a2a87833191e4bcf89bb0365e83a8be3a58b31fb7f38"}, 94 | {file = "aiohttp-3.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:b791a3143681a520c0a17e26ae7465f1b6f99461a28019d1a2f425236e6eedb5"}, 95 | {file = "aiohttp-3.9.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0ed621426d961df79aa3b963ac7af0d40392956ffa9be022024cd16297b30c8c"}, 96 | {file = "aiohttp-3.9.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7f46acd6a194287b7e41e87957bfe2ad1ad88318d447caf5b090012f2c5bb528"}, 97 | {file = "aiohttp-3.9.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:feeb18a801aacb098220e2c3eea59a512362eb408d4afd0c242044c33ad6d542"}, 98 | {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f734e38fd8666f53da904c52a23ce517f1b07722118d750405af7e4123933511"}, 99 | {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b40670ec7e2156d8e57f70aec34a7216407848dfe6c693ef131ddf6e76feb672"}, 100 | {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fdd215b7b7fd4a53994f238d0f46b7ba4ac4c0adb12452beee724ddd0743ae5d"}, 101 | {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:017a21b0df49039c8f46ca0971b3a7fdc1f56741ab1240cb90ca408049766168"}, 102 | {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e99abf0bba688259a496f966211c49a514e65afa9b3073a1fcee08856e04425b"}, 103 | {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:648056db9a9fa565d3fa851880f99f45e3f9a771dd3ff3bb0c048ea83fb28194"}, 104 | {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8aacb477dc26797ee089721536a292a664846489c49d3ef9725f992449eda5a8"}, 105 | {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:522a11c934ea660ff8953eda090dcd2154d367dec1ae3c540aff9f8a5c109ab4"}, 106 | {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:5bce0dc147ca85caa5d33debc4f4d65e8e8b5c97c7f9f660f215fa74fc49a321"}, 107 | {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b4af9f25b49a7be47c0972139e59ec0e8285c371049df1a63b6ca81fdd216a2"}, 108 | {file = "aiohttp-3.9.3-cp38-cp38-win32.whl", hash = "sha256:298abd678033b8571995650ccee753d9458dfa0377be4dba91e4491da3f2be63"}, 109 | {file = "aiohttp-3.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:69361bfdca5468c0488d7017b9b1e5ce769d40b46a9f4a2eed26b78619e9396c"}, 110 | {file = "aiohttp-3.9.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0fa43c32d1643f518491d9d3a730f85f5bbaedcbd7fbcae27435bb8b7a061b29"}, 111 | {file = "aiohttp-3.9.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:835a55b7ca49468aaaac0b217092dfdff370e6c215c9224c52f30daaa735c1c1"}, 112 | {file = "aiohttp-3.9.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06a9b2c8837d9a94fae16c6223acc14b4dfdff216ab9b7202e07a9a09541168f"}, 113 | {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abf151955990d23f84205286938796c55ff11bbfb4ccfada8c9c83ae6b3c89a3"}, 114 | {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59c26c95975f26e662ca78fdf543d4eeaef70e533a672b4113dd888bd2423caa"}, 115 | {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f95511dd5d0e05fd9728bac4096319f80615aaef4acbecb35a990afebe953b0e"}, 116 | {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:595f105710293e76b9dc09f52e0dd896bd064a79346234b521f6b968ffdd8e58"}, 117 | {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7c8b816c2b5af5c8a436df44ca08258fc1a13b449393a91484225fcb7545533"}, 118 | {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f1088fa100bf46e7b398ffd9904f4808a0612e1d966b4aa43baa535d1b6341eb"}, 119 | {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f59dfe57bb1ec82ac0698ebfcdb7bcd0e99c255bd637ff613760d5f33e7c81b3"}, 120 | {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:361a1026c9dd4aba0109e4040e2aecf9884f5cfe1b1b1bd3d09419c205e2e53d"}, 121 | {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:363afe77cfcbe3a36353d8ea133e904b108feea505aa4792dad6585a8192c55a"}, 122 | {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e2c45c208c62e955e8256949eb225bd8b66a4c9b6865729a786f2aa79b72e9d"}, 123 | {file = "aiohttp-3.9.3-cp39-cp39-win32.whl", hash = "sha256:f7217af2e14da0856e082e96ff637f14ae45c10a5714b63c77f26d8884cf1051"}, 124 | {file = "aiohttp-3.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:27468897f628c627230dba07ec65dc8d0db566923c48f29e084ce382119802bc"}, 125 | {file = "aiohttp-3.9.3.tar.gz", hash = "sha256:90842933e5d1ff760fae6caca4b2b3edba53ba8f4b71e95dacf2818a2aca06f7"}, 126 | ] 127 | 128 | [package.dependencies] 129 | aiosignal = ">=1.1.2" 130 | attrs = ">=17.3.0" 131 | frozenlist = ">=1.1.1" 132 | multidict = ">=4.5,<7.0" 133 | yarl = ">=1.0,<2.0" 134 | 135 | [package.extras] 136 | speedups = ["Brotli", "aiodns", "brotlicffi"] 137 | 138 | [[package]] 139 | name = "aiosignal" 140 | version = "1.3.1" 141 | description = "aiosignal: a list of registered asynchronous callbacks" 142 | optional = false 143 | python-versions = ">=3.7" 144 | files = [ 145 | {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, 146 | {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, 147 | ] 148 | 149 | [package.dependencies] 150 | frozenlist = ">=1.1.0" 151 | 152 | [[package]] 153 | name = "annotated-types" 154 | version = "0.6.0" 155 | description = "Reusable constraint types to use with typing.Annotated" 156 | optional = false 157 | python-versions = ">=3.8" 158 | files = [ 159 | {file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"}, 160 | {file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"}, 161 | ] 162 | 163 | [[package]] 164 | name = "attrs" 165 | version = "23.2.0" 166 | description = "Classes Without Boilerplate" 167 | optional = false 168 | python-versions = ">=3.7" 169 | files = [ 170 | {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, 171 | {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, 172 | ] 173 | 174 | [package.extras] 175 | cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] 176 | dev = ["attrs[tests]", "pre-commit"] 177 | docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] 178 | tests = ["attrs[tests-no-zope]", "zope-interface"] 179 | tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] 180 | tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] 181 | 182 | [[package]] 183 | name = "black" 184 | version = "24.3.0" 185 | description = "The uncompromising code formatter." 186 | optional = false 187 | python-versions = ">=3.8" 188 | files = [ 189 | {file = "black-24.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7d5e026f8da0322b5662fa7a8e752b3fa2dac1c1cbc213c3d7ff9bdd0ab12395"}, 190 | {file = "black-24.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9f50ea1132e2189d8dff0115ab75b65590a3e97de1e143795adb4ce317934995"}, 191 | {file = "black-24.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2af80566f43c85f5797365077fb64a393861a3730bd110971ab7a0c94e873e7"}, 192 | {file = "black-24.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:4be5bb28e090456adfc1255e03967fb67ca846a03be7aadf6249096100ee32d0"}, 193 | {file = "black-24.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4f1373a7808a8f135b774039f61d59e4be7eb56b2513d3d2f02a8b9365b8a8a9"}, 194 | {file = "black-24.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:aadf7a02d947936ee418777e0247ea114f78aff0d0959461057cae8a04f20597"}, 195 | {file = "black-24.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c02e4ea2ae09d16314d30912a58ada9a5c4fdfedf9512d23326128ac08ac3d"}, 196 | {file = "black-24.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:bf21b7b230718a5f08bd32d5e4f1db7fc8788345c8aea1d155fc17852b3410f5"}, 197 | {file = "black-24.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:2818cf72dfd5d289e48f37ccfa08b460bf469e67fb7c4abb07edc2e9f16fb63f"}, 198 | {file = "black-24.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4acf672def7eb1725f41f38bf6bf425c8237248bb0804faa3965c036f7672d11"}, 199 | {file = "black-24.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7ed6668cbbfcd231fa0dc1b137d3e40c04c7f786e626b405c62bcd5db5857e4"}, 200 | {file = "black-24.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:56f52cfbd3dabe2798d76dbdd299faa046a901041faf2cf33288bc4e6dae57b5"}, 201 | {file = "black-24.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:79dcf34b33e38ed1b17434693763301d7ccbd1c5860674a8f871bd15139e7837"}, 202 | {file = "black-24.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e19cb1c6365fd6dc38a6eae2dcb691d7d83935c10215aef8e6c38edee3f77abd"}, 203 | {file = "black-24.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65b76c275e4c1c5ce6e9870911384bff5ca31ab63d19c76811cb1fb162678213"}, 204 | {file = "black-24.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:b5991d523eee14756f3c8d5df5231550ae8993e2286b8014e2fdea7156ed0959"}, 205 | {file = "black-24.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c45f8dff244b3c431b36e3224b6be4a127c6aca780853574c00faf99258041eb"}, 206 | {file = "black-24.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6905238a754ceb7788a73f02b45637d820b2f5478b20fec82ea865e4f5d4d9f7"}, 207 | {file = "black-24.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7de8d330763c66663661a1ffd432274a2f92f07feeddd89ffd085b5744f85e7"}, 208 | {file = "black-24.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:7bb041dca0d784697af4646d3b62ba4a6b028276ae878e53f6b4f74ddd6db99f"}, 209 | {file = "black-24.3.0-py3-none-any.whl", hash = "sha256:41622020d7120e01d377f74249e677039d20e6344ff5851de8a10f11f513bf93"}, 210 | {file = "black-24.3.0.tar.gz", hash = "sha256:a0c9c4a0771afc6919578cec71ce82a3e31e054904e7197deacbc9382671c41f"}, 211 | ] 212 | 213 | [package.dependencies] 214 | click = ">=8.0.0" 215 | mypy-extensions = ">=0.4.3" 216 | packaging = ">=22.0" 217 | pathspec = ">=0.9.0" 218 | platformdirs = ">=2" 219 | 220 | [package.extras] 221 | colorama = ["colorama (>=0.4.3)"] 222 | d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] 223 | jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] 224 | uvloop = ["uvloop (>=0.15.2)"] 225 | 226 | [[package]] 227 | name = "certifi" 228 | version = "2024.2.2" 229 | description = "Python package for providing Mozilla's CA Bundle." 230 | optional = false 231 | python-versions = ">=3.6" 232 | files = [ 233 | {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, 234 | {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, 235 | ] 236 | 237 | [[package]] 238 | name = "click" 239 | version = "8.1.7" 240 | description = "Composable command line interface toolkit" 241 | optional = false 242 | python-versions = ">=3.7" 243 | files = [ 244 | {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, 245 | {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, 246 | ] 247 | 248 | [package.dependencies] 249 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 250 | 251 | [[package]] 252 | name = "colorama" 253 | version = "0.4.6" 254 | description = "Cross-platform colored terminal text." 255 | optional = false 256 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 257 | files = [ 258 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 259 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 260 | ] 261 | 262 | [[package]] 263 | name = "dnspython" 264 | version = "2.6.1" 265 | description = "DNS toolkit" 266 | optional = false 267 | python-versions = ">=3.8" 268 | files = [ 269 | {file = "dnspython-2.6.1-py3-none-any.whl", hash = "sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50"}, 270 | {file = "dnspython-2.6.1.tar.gz", hash = "sha256:e8f0f9c23a7b7cb99ded64e6c3a6f3e701d78f50c55e002b839dea7225cff7cc"}, 271 | ] 272 | 273 | [package.extras] 274 | dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "mypy (>=1.8)", "pylint (>=3)", "pytest (>=7.4)", "pytest-cov (>=4.1.0)", "sphinx (>=7.2.0)", "twine (>=4.0.0)", "wheel (>=0.42.0)"] 275 | dnssec = ["cryptography (>=41)"] 276 | doh = ["h2 (>=4.1.0)", "httpcore (>=1.0.0)", "httpx (>=0.26.0)"] 277 | doq = ["aioquic (>=0.9.25)"] 278 | idna = ["idna (>=3.6)"] 279 | trio = ["trio (>=0.23)"] 280 | wmi = ["wmi (>=1.5.1)"] 281 | 282 | [[package]] 283 | name = "email-validator" 284 | version = "2.1.1" 285 | description = "A robust email address syntax and deliverability validation library." 286 | optional = false 287 | python-versions = ">=3.8" 288 | files = [ 289 | {file = "email_validator-2.1.1-py3-none-any.whl", hash = "sha256:97d882d174e2a65732fb43bfce81a3a834cbc1bde8bf419e30ef5ea976370a05"}, 290 | {file = "email_validator-2.1.1.tar.gz", hash = "sha256:200a70680ba08904be6d1eef729205cc0d687634399a5924d842533efb824b84"}, 291 | ] 292 | 293 | [package.dependencies] 294 | dnspython = ">=2.0.0" 295 | idna = ">=2.0.0" 296 | 297 | [[package]] 298 | name = "frozenlist" 299 | version = "1.4.1" 300 | description = "A list-like structure which implements collections.abc.MutableSequence" 301 | optional = false 302 | python-versions = ">=3.8" 303 | files = [ 304 | {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac"}, 305 | {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868"}, 306 | {file = "frozenlist-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776"}, 307 | {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a"}, 308 | {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad"}, 309 | {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c"}, 310 | {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe"}, 311 | {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"}, 312 | {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98"}, 313 | {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75"}, 314 | {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5"}, 315 | {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950"}, 316 | {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc"}, 317 | {file = "frozenlist-1.4.1-cp310-cp310-win32.whl", hash = "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1"}, 318 | {file = "frozenlist-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439"}, 319 | {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0"}, 320 | {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49"}, 321 | {file = "frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced"}, 322 | {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0"}, 323 | {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106"}, 324 | {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068"}, 325 | {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2"}, 326 | {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"}, 327 | {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82"}, 328 | {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec"}, 329 | {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a"}, 330 | {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74"}, 331 | {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2"}, 332 | {file = "frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17"}, 333 | {file = "frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825"}, 334 | {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae"}, 335 | {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb"}, 336 | {file = "frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b"}, 337 | {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86"}, 338 | {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480"}, 339 | {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09"}, 340 | {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a"}, 341 | {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"}, 342 | {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6"}, 343 | {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1"}, 344 | {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b"}, 345 | {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e"}, 346 | {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8"}, 347 | {file = "frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89"}, 348 | {file = "frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5"}, 349 | {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d"}, 350 | {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826"}, 351 | {file = "frozenlist-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb"}, 352 | {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6"}, 353 | {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d"}, 354 | {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887"}, 355 | {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a"}, 356 | {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"}, 357 | {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701"}, 358 | {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0"}, 359 | {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11"}, 360 | {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09"}, 361 | {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7"}, 362 | {file = "frozenlist-1.4.1-cp38-cp38-win32.whl", hash = "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497"}, 363 | {file = "frozenlist-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09"}, 364 | {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e"}, 365 | {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d"}, 366 | {file = "frozenlist-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8"}, 367 | {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0"}, 368 | {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b"}, 369 | {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0"}, 370 | {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897"}, 371 | {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"}, 372 | {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742"}, 373 | {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea"}, 374 | {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5"}, 375 | {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9"}, 376 | {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6"}, 377 | {file = "frozenlist-1.4.1-cp39-cp39-win32.whl", hash = "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932"}, 378 | {file = "frozenlist-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0"}, 379 | {file = "frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7"}, 380 | {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}, 381 | ] 382 | 383 | [[package]] 384 | name = "idna" 385 | version = "3.6" 386 | description = "Internationalized Domain Names in Applications (IDNA)" 387 | optional = false 388 | python-versions = ">=3.5" 389 | files = [ 390 | {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, 391 | {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, 392 | ] 393 | 394 | [[package]] 395 | name = "magic-filter" 396 | version = "1.0.12" 397 | description = "" 398 | optional = false 399 | python-versions = ">=3.7" 400 | files = [ 401 | {file = "magic_filter-1.0.12-py3-none-any.whl", hash = "sha256:e5929e544f310c2b1f154318db8c5cdf544dd658efa998172acd2e4ba0f6c6a6"}, 402 | {file = "magic_filter-1.0.12.tar.gz", hash = "sha256:4751d0b579a5045d1dc250625c4c508c18c3def5ea6afaf3957cb4530d03f7f9"}, 403 | ] 404 | 405 | [package.extras] 406 | dev = ["black (>=22.8.0,<22.9.0)", "flake8 (>=5.0.4,<5.1.0)", "isort (>=5.11.5,<5.12.0)", "mypy (>=1.4.1,<1.5.0)", "pre-commit (>=2.20.0,<2.21.0)", "pytest (>=7.1.3,<7.2.0)", "pytest-cov (>=3.0.0,<3.1.0)", "pytest-html (>=3.1.1,<3.2.0)", "types-setuptools (>=65.3.0,<65.4.0)"] 407 | 408 | [[package]] 409 | name = "multidict" 410 | version = "6.0.5" 411 | description = "multidict implementation" 412 | optional = false 413 | python-versions = ">=3.7" 414 | files = [ 415 | {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9"}, 416 | {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604"}, 417 | {file = "multidict-6.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600"}, 418 | {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c"}, 419 | {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5"}, 420 | {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f"}, 421 | {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae"}, 422 | {file = "multidict-6.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182"}, 423 | {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf"}, 424 | {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442"}, 425 | {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a"}, 426 | {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef"}, 427 | {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc"}, 428 | {file = "multidict-6.0.5-cp310-cp310-win32.whl", hash = "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319"}, 429 | {file = "multidict-6.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8"}, 430 | {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba"}, 431 | {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e"}, 432 | {file = "multidict-6.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd"}, 433 | {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3"}, 434 | {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf"}, 435 | {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29"}, 436 | {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed"}, 437 | {file = "multidict-6.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733"}, 438 | {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f"}, 439 | {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4"}, 440 | {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1"}, 441 | {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc"}, 442 | {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e"}, 443 | {file = "multidict-6.0.5-cp311-cp311-win32.whl", hash = "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c"}, 444 | {file = "multidict-6.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea"}, 445 | {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e"}, 446 | {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b"}, 447 | {file = "multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5"}, 448 | {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450"}, 449 | {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496"}, 450 | {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a"}, 451 | {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226"}, 452 | {file = "multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271"}, 453 | {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb"}, 454 | {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef"}, 455 | {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24"}, 456 | {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6"}, 457 | {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda"}, 458 | {file = "multidict-6.0.5-cp312-cp312-win32.whl", hash = "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5"}, 459 | {file = "multidict-6.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556"}, 460 | {file = "multidict-6.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:19fe01cea168585ba0f678cad6f58133db2aa14eccaf22f88e4a6dccadfad8b3"}, 461 | {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf7a982604375a8d49b6cc1b781c1747f243d91b81035a9b43a2126c04766f5"}, 462 | {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:107c0cdefe028703fb5dafe640a409cb146d44a6ae201e55b35a4af8e95457dd"}, 463 | {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:403c0911cd5d5791605808b942c88a8155c2592e05332d2bf78f18697a5fa15e"}, 464 | {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aeaf541ddbad8311a87dd695ed9642401131ea39ad7bc8cf3ef3967fd093b626"}, 465 | {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4972624066095e52b569e02b5ca97dbd7a7ddd4294bf4e7247d52635630dd83"}, 466 | {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d946b0a9eb8aaa590df1fe082cee553ceab173e6cb5b03239716338629c50c7a"}, 467 | {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b55358304d7a73d7bdf5de62494aaf70bd33015831ffd98bc498b433dfe5b10c"}, 468 | {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:a3145cb08d8625b2d3fee1b2d596a8766352979c9bffe5d7833e0503d0f0b5e5"}, 469 | {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d65f25da8e248202bd47445cec78e0025c0fe7582b23ec69c3b27a640dd7a8e3"}, 470 | {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c9bf56195c6bbd293340ea82eafd0071cb3d450c703d2c93afb89f93b8386ccc"}, 471 | {file = "multidict-6.0.5-cp37-cp37m-win32.whl", hash = "sha256:69db76c09796b313331bb7048229e3bee7928eb62bab5e071e9f7fcc4879caee"}, 472 | {file = "multidict-6.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:fce28b3c8a81b6b36dfac9feb1de115bab619b3c13905b419ec71d03a3fc1423"}, 473 | {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76f067f5121dcecf0d63a67f29080b26c43c71a98b10c701b0677e4a065fbd54"}, 474 | {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b82cc8ace10ab5bd93235dfaab2021c70637005e1ac787031f4d1da63d493c1d"}, 475 | {file = "multidict-6.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cb241881eefd96b46f89b1a056187ea8e9ba14ab88ba632e68d7a2ecb7aadf7"}, 476 | {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8e94e6912639a02ce173341ff62cc1201232ab86b8a8fcc05572741a5dc7d93"}, 477 | {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09a892e4a9fb47331da06948690ae38eaa2426de97b4ccbfafbdcbe5c8f37ff8"}, 478 | {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55205d03e8a598cfc688c71ca8ea5f66447164efff8869517f175ea632c7cb7b"}, 479 | {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37b15024f864916b4951adb95d3a80c9431299080341ab9544ed148091b53f50"}, 480 | {file = "multidict-6.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2a1dee728b52b33eebff5072817176c172050d44d67befd681609b4746e1c2e"}, 481 | {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:edd08e6f2f1a390bf137080507e44ccc086353c8e98c657e666c017718561b89"}, 482 | {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:60d698e8179a42ec85172d12f50b1668254628425a6bd611aba022257cac1386"}, 483 | {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3d25f19500588cbc47dc19081d78131c32637c25804df8414463ec908631e453"}, 484 | {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4cc0ef8b962ac7a5e62b9e826bd0cd5040e7d401bc45a6835910ed699037a461"}, 485 | {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:eca2e9d0cc5a889850e9bbd68e98314ada174ff6ccd1129500103df7a94a7a44"}, 486 | {file = "multidict-6.0.5-cp38-cp38-win32.whl", hash = "sha256:4a6a4f196f08c58c59e0b8ef8ec441d12aee4125a7d4f4fef000ccb22f8d7241"}, 487 | {file = "multidict-6.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:0275e35209c27a3f7951e1ce7aaf93ce0d163b28948444bec61dd7badc6d3f8c"}, 488 | {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e7be68734bd8c9a513f2b0cfd508802d6609da068f40dc57d4e3494cefc92929"}, 489 | {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1d9ea7a7e779d7a3561aade7d596649fbecfa5c08a7674b11b423783217933f9"}, 490 | {file = "multidict-6.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ea1456df2a27c73ce51120fa2f519f1bea2f4a03a917f4a43c8707cf4cbbae1a"}, 491 | {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf590b134eb70629e350691ecca88eac3e3b8b3c86992042fb82e3cb1830d5e1"}, 492 | {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c0631926c4f58e9a5ccce555ad7747d9a9f8b10619621f22f9635f069f6233e"}, 493 | {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dce1c6912ab9ff5f179eaf6efe7365c1f425ed690b03341911bf4939ef2f3046"}, 494 | {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0868d64af83169e4d4152ec612637a543f7a336e4a307b119e98042e852ad9c"}, 495 | {file = "multidict-6.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:141b43360bfd3bdd75f15ed811850763555a251e38b2405967f8e25fb43f7d40"}, 496 | {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7df704ca8cf4a073334e0427ae2345323613e4df18cc224f647f251e5e75a527"}, 497 | {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6214c5a5571802c33f80e6c84713b2c79e024995b9c5897f794b43e714daeec9"}, 498 | {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd6c8fca38178e12c00418de737aef1261576bd1b6e8c6134d3e729a4e858b38"}, 499 | {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e02021f87a5b6932fa6ce916ca004c4d441509d33bbdbeca70d05dff5e9d2479"}, 500 | {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ebd8d160f91a764652d3e51ce0d2956b38efe37c9231cd82cfc0bed2e40b581c"}, 501 | {file = "multidict-6.0.5-cp39-cp39-win32.whl", hash = "sha256:04da1bb8c8dbadf2a18a452639771951c662c5ad03aefe4884775454be322c9b"}, 502 | {file = "multidict-6.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:d6f6d4f185481c9669b9447bf9d9cf3b95a0e9df9d169bbc17e363b7d5487755"}, 503 | {file = "multidict-6.0.5-py3-none-any.whl", hash = "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7"}, 504 | {file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"}, 505 | ] 506 | 507 | [[package]] 508 | name = "mypy-extensions" 509 | version = "1.0.0" 510 | description = "Type system extensions for programs checked with the mypy type checker." 511 | optional = false 512 | python-versions = ">=3.5" 513 | files = [ 514 | {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, 515 | {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, 516 | ] 517 | 518 | [[package]] 519 | name = "packaging" 520 | version = "24.0" 521 | description = "Core utilities for Python packages" 522 | optional = false 523 | python-versions = ">=3.7" 524 | files = [ 525 | {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, 526 | {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, 527 | ] 528 | 529 | [[package]] 530 | name = "pathspec" 531 | version = "0.12.1" 532 | description = "Utility library for gitignore style pattern matching of file paths." 533 | optional = false 534 | python-versions = ">=3.8" 535 | files = [ 536 | {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, 537 | {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, 538 | ] 539 | 540 | [[package]] 541 | name = "platformdirs" 542 | version = "4.2.0" 543 | description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 544 | optional = false 545 | python-versions = ">=3.8" 546 | files = [ 547 | {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, 548 | {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, 549 | ] 550 | 551 | [package.extras] 552 | docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] 553 | test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] 554 | 555 | [[package]] 556 | name = "pydantic" 557 | version = "2.5.3" 558 | description = "Data validation using Python type hints" 559 | optional = false 560 | python-versions = ">=3.7" 561 | files = [ 562 | {file = "pydantic-2.5.3-py3-none-any.whl", hash = "sha256:d0caf5954bee831b6bfe7e338c32b9e30c85dfe080c843680783ac2b631673b4"}, 563 | {file = "pydantic-2.5.3.tar.gz", hash = "sha256:b3ef57c62535b0941697cce638c08900d87fcb67e29cfa99e8a68f747f393f7a"}, 564 | ] 565 | 566 | [package.dependencies] 567 | annotated-types = ">=0.4.0" 568 | pydantic-core = "2.14.6" 569 | typing-extensions = ">=4.6.1" 570 | 571 | [package.extras] 572 | email = ["email-validator (>=2.0.0)"] 573 | 574 | [[package]] 575 | name = "pydantic-core" 576 | version = "2.14.6" 577 | description = "" 578 | optional = false 579 | python-versions = ">=3.7" 580 | files = [ 581 | {file = "pydantic_core-2.14.6-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:72f9a942d739f09cd42fffe5dc759928217649f070056f03c70df14f5770acf9"}, 582 | {file = "pydantic_core-2.14.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6a31d98c0d69776c2576dda4b77b8e0c69ad08e8b539c25c7d0ca0dc19a50d6c"}, 583 | {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5aa90562bc079c6c290f0512b21768967f9968e4cfea84ea4ff5af5d917016e4"}, 584 | {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:370ffecb5316ed23b667d99ce4debe53ea664b99cc37bfa2af47bc769056d534"}, 585 | {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f85f3843bdb1fe80e8c206fe6eed7a1caeae897e496542cee499c374a85c6e08"}, 586 | {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9862bf828112e19685b76ca499b379338fd4c5c269d897e218b2ae8fcb80139d"}, 587 | {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:036137b5ad0cb0004c75b579445a1efccd072387a36c7f217bb8efd1afbe5245"}, 588 | {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:92879bce89f91f4b2416eba4429c7b5ca22c45ef4a499c39f0c5c69257522c7c"}, 589 | {file = "pydantic_core-2.14.6-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0c08de15d50fa190d577e8591f0329a643eeaed696d7771760295998aca6bc66"}, 590 | {file = "pydantic_core-2.14.6-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:36099c69f6b14fc2c49d7996cbf4f87ec4f0e66d1c74aa05228583225a07b590"}, 591 | {file = "pydantic_core-2.14.6-cp310-none-win32.whl", hash = "sha256:7be719e4d2ae6c314f72844ba9d69e38dff342bc360379f7c8537c48e23034b7"}, 592 | {file = "pydantic_core-2.14.6-cp310-none-win_amd64.whl", hash = "sha256:36fa402dcdc8ea7f1b0ddcf0df4254cc6b2e08f8cd80e7010d4c4ae6e86b2a87"}, 593 | {file = "pydantic_core-2.14.6-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:dea7fcd62915fb150cdc373212141a30037e11b761fbced340e9db3379b892d4"}, 594 | {file = "pydantic_core-2.14.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ffff855100bc066ff2cd3aa4a60bc9534661816b110f0243e59503ec2df38421"}, 595 | {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b027c86c66b8627eb90e57aee1f526df77dc6d8b354ec498be9a757d513b92b"}, 596 | {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:00b1087dabcee0b0ffd104f9f53d7d3eaddfaa314cdd6726143af6bc713aa27e"}, 597 | {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:75ec284328b60a4e91010c1acade0c30584f28a1f345bc8f72fe8b9e46ec6a96"}, 598 | {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e1f4744eea1501404b20b0ac059ff7e3f96a97d3e3f48ce27a139e053bb370b"}, 599 | {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2602177668f89b38b9f84b7b3435d0a72511ddef45dc14446811759b82235a1"}, 600 | {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6c8edaea3089bf908dd27da8f5d9e395c5b4dc092dbcce9b65e7156099b4b937"}, 601 | {file = "pydantic_core-2.14.6-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:478e9e7b360dfec451daafe286998d4a1eeaecf6d69c427b834ae771cad4b622"}, 602 | {file = "pydantic_core-2.14.6-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b6ca36c12a5120bad343eef193cc0122928c5c7466121da7c20f41160ba00ba2"}, 603 | {file = "pydantic_core-2.14.6-cp311-none-win32.whl", hash = "sha256:2b8719037e570639e6b665a4050add43134d80b687288ba3ade18b22bbb29dd2"}, 604 | {file = "pydantic_core-2.14.6-cp311-none-win_amd64.whl", hash = "sha256:78ee52ecc088c61cce32b2d30a826f929e1708f7b9247dc3b921aec367dc1b23"}, 605 | {file = "pydantic_core-2.14.6-cp311-none-win_arm64.whl", hash = "sha256:a19b794f8fe6569472ff77602437ec4430f9b2b9ec7a1105cfd2232f9ba355e6"}, 606 | {file = "pydantic_core-2.14.6-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:667aa2eac9cd0700af1ddb38b7b1ef246d8cf94c85637cbb03d7757ca4c3fdec"}, 607 | {file = "pydantic_core-2.14.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cdee837710ef6b56ebd20245b83799fce40b265b3b406e51e8ccc5b85b9099b7"}, 608 | {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c5bcf3414367e29f83fd66f7de64509a8fd2368b1edf4351e862910727d3e51"}, 609 | {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:26a92ae76f75d1915806b77cf459811e772d8f71fd1e4339c99750f0e7f6324f"}, 610 | {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a983cca5ed1dd9a35e9e42ebf9f278d344603bfcb174ff99a5815f953925140a"}, 611 | {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cb92f9061657287eded380d7dc455bbf115430b3aa4741bdc662d02977e7d0af"}, 612 | {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4ace1e220b078c8e48e82c081e35002038657e4b37d403ce940fa679e57113b"}, 613 | {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ef633add81832f4b56d3b4c9408b43d530dfca29e68fb1b797dcb861a2c734cd"}, 614 | {file = "pydantic_core-2.14.6-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7e90d6cc4aad2cc1f5e16ed56e46cebf4877c62403a311af20459c15da76fd91"}, 615 | {file = "pydantic_core-2.14.6-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e8a5ac97ea521d7bde7621d86c30e86b798cdecd985723c4ed737a2aa9e77d0c"}, 616 | {file = "pydantic_core-2.14.6-cp312-none-win32.whl", hash = "sha256:f27207e8ca3e5e021e2402ba942e5b4c629718e665c81b8b306f3c8b1ddbb786"}, 617 | {file = "pydantic_core-2.14.6-cp312-none-win_amd64.whl", hash = "sha256:b3e5fe4538001bb82e2295b8d2a39356a84694c97cb73a566dc36328b9f83b40"}, 618 | {file = "pydantic_core-2.14.6-cp312-none-win_arm64.whl", hash = "sha256:64634ccf9d671c6be242a664a33c4acf12882670b09b3f163cd00a24cffbd74e"}, 619 | {file = "pydantic_core-2.14.6-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:24368e31be2c88bd69340fbfe741b405302993242ccb476c5c3ff48aeee1afe0"}, 620 | {file = "pydantic_core-2.14.6-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:e33b0834f1cf779aa839975f9d8755a7c2420510c0fa1e9fa0497de77cd35d2c"}, 621 | {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6af4b3f52cc65f8a0bc8b1cd9676f8c21ef3e9132f21fed250f6958bd7223bed"}, 622 | {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d15687d7d7f40333bd8266f3814c591c2e2cd263fa2116e314f60d82086e353a"}, 623 | {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:095b707bb287bfd534044166ab767bec70a9bba3175dcdc3371782175c14e43c"}, 624 | {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94fc0e6621e07d1e91c44e016cc0b189b48db053061cc22d6298a611de8071bb"}, 625 | {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ce830e480f6774608dedfd4a90c42aac4a7af0a711f1b52f807130c2e434c06"}, 626 | {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a306cdd2ad3a7d795d8e617a58c3a2ed0f76c8496fb7621b6cd514eb1532cae8"}, 627 | {file = "pydantic_core-2.14.6-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:2f5fa187bde8524b1e37ba894db13aadd64faa884657473b03a019f625cee9a8"}, 628 | {file = "pydantic_core-2.14.6-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:438027a975cc213a47c5d70672e0d29776082155cfae540c4e225716586be75e"}, 629 | {file = "pydantic_core-2.14.6-cp37-none-win32.whl", hash = "sha256:f96ae96a060a8072ceff4cfde89d261837b4294a4f28b84a28765470d502ccc6"}, 630 | {file = "pydantic_core-2.14.6-cp37-none-win_amd64.whl", hash = "sha256:e646c0e282e960345314f42f2cea5e0b5f56938c093541ea6dbf11aec2862391"}, 631 | {file = "pydantic_core-2.14.6-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:db453f2da3f59a348f514cfbfeb042393b68720787bbef2b4c6068ea362c8149"}, 632 | {file = "pydantic_core-2.14.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3860c62057acd95cc84044e758e47b18dcd8871a328ebc8ccdefd18b0d26a21b"}, 633 | {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36026d8f99c58d7044413e1b819a67ca0e0b8ebe0f25e775e6c3d1fabb3c38fb"}, 634 | {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8ed1af8692bd8d2a29d702f1a2e6065416d76897d726e45a1775b1444f5928a7"}, 635 | {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:314ccc4264ce7d854941231cf71b592e30d8d368a71e50197c905874feacc8a8"}, 636 | {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:982487f8931067a32e72d40ab6b47b1628a9c5d344be7f1a4e668fb462d2da42"}, 637 | {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dbe357bc4ddda078f79d2a36fc1dd0494a7f2fad83a0a684465b6f24b46fe80"}, 638 | {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2f6ffc6701a0eb28648c845f4945a194dc7ab3c651f535b81793251e1185ac3d"}, 639 | {file = "pydantic_core-2.14.6-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7f5025db12fc6de7bc1104d826d5aee1d172f9ba6ca936bf6474c2148ac336c1"}, 640 | {file = "pydantic_core-2.14.6-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dab03ed811ed1c71d700ed08bde8431cf429bbe59e423394f0f4055f1ca0ea60"}, 641 | {file = "pydantic_core-2.14.6-cp38-none-win32.whl", hash = "sha256:dfcbebdb3c4b6f739a91769aea5ed615023f3c88cb70df812849aef634c25fbe"}, 642 | {file = "pydantic_core-2.14.6-cp38-none-win_amd64.whl", hash = "sha256:99b14dbea2fdb563d8b5a57c9badfcd72083f6006caf8e126b491519c7d64ca8"}, 643 | {file = "pydantic_core-2.14.6-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:4ce8299b481bcb68e5c82002b96e411796b844d72b3e92a3fbedfe8e19813eab"}, 644 | {file = "pydantic_core-2.14.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b9a9d92f10772d2a181b5ca339dee066ab7d1c9a34ae2421b2a52556e719756f"}, 645 | {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd9e98b408384989ea4ab60206b8e100d8687da18b5c813c11e92fd8212a98e0"}, 646 | {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4f86f1f318e56f5cbb282fe61eb84767aee743ebe32c7c0834690ebea50c0a6b"}, 647 | {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86ce5fcfc3accf3a07a729779d0b86c5d0309a4764c897d86c11089be61da160"}, 648 | {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dcf1978be02153c6a31692d4fbcc2a3f1db9da36039ead23173bc256ee3b91b"}, 649 | {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eedf97be7bc3dbc8addcef4142f4b4164066df0c6f36397ae4aaed3eb187d8ab"}, 650 | {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d5f916acf8afbcab6bacbb376ba7dc61f845367901ecd5e328fc4d4aef2fcab0"}, 651 | {file = "pydantic_core-2.14.6-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8a14c192c1d724c3acbfb3f10a958c55a2638391319ce8078cb36c02283959b9"}, 652 | {file = "pydantic_core-2.14.6-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0348b1dc6b76041516e8a854ff95b21c55f5a411c3297d2ca52f5528e49d8411"}, 653 | {file = "pydantic_core-2.14.6-cp39-none-win32.whl", hash = "sha256:de2a0645a923ba57c5527497daf8ec5df69c6eadf869e9cd46e86349146e5975"}, 654 | {file = "pydantic_core-2.14.6-cp39-none-win_amd64.whl", hash = "sha256:aca48506a9c20f68ee61c87f2008f81f8ee99f8d7f0104bff3c47e2d148f89d9"}, 655 | {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:d5c28525c19f5bb1e09511669bb57353d22b94cf8b65f3a8d141c389a55dec95"}, 656 | {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:78d0768ee59baa3de0f4adac9e3748b4b1fffc52143caebddfd5ea2961595277"}, 657 | {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b93785eadaef932e4fe9c6e12ba67beb1b3f1e5495631419c784ab87e975670"}, 658 | {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a874f21f87c485310944b2b2734cd6d318765bcbb7515eead33af9641816506e"}, 659 | {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b89f4477d915ea43b4ceea6756f63f0288941b6443a2b28c69004fe07fde0d0d"}, 660 | {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:172de779e2a153d36ee690dbc49c6db568d7b33b18dc56b69a7514aecbcf380d"}, 661 | {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:dfcebb950aa7e667ec226a442722134539e77c575f6cfaa423f24371bb8d2e94"}, 662 | {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:55a23dcd98c858c0db44fc5c04fc7ed81c4b4d33c653a7c45ddaebf6563a2f66"}, 663 | {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:4241204e4b36ab5ae466ecec5c4c16527a054c69f99bba20f6f75232a6a534e2"}, 664 | {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e574de99d735b3fc8364cba9912c2bec2da78775eba95cbb225ef7dda6acea24"}, 665 | {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1302a54f87b5cd8528e4d6d1bf2133b6aa7c6122ff8e9dc5220fbc1e07bffebd"}, 666 | {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8e81e4b55930e5ffab4a68db1af431629cf2e4066dbdbfef65348b8ab804ea8"}, 667 | {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c99462ffc538717b3e60151dfaf91125f637e801f5ab008f81c402f1dff0cd0f"}, 668 | {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e4cf2d5829f6963a5483ec01578ee76d329eb5caf330ecd05b3edd697e7d768a"}, 669 | {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:cf10b7d58ae4a1f07fccbf4a0a956d705356fea05fb4c70608bb6fa81d103cda"}, 670 | {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:399ac0891c284fa8eb998bcfa323f2234858f5d2efca3950ae58c8f88830f145"}, 671 | {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c6a5c79b28003543db3ba67d1df336f253a87d3112dac3a51b94f7d48e4c0e1"}, 672 | {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:599c87d79cab2a6a2a9df4aefe0455e61e7d2aeede2f8577c1b7c0aec643ee8e"}, 673 | {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43e166ad47ba900f2542a80d83f9fc65fe99eb63ceec4debec160ae729824052"}, 674 | {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3a0b5db001b98e1c649dd55afa928e75aa4087e587b9524a4992316fa23c9fba"}, 675 | {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:747265448cb57a9f37572a488a57d873fd96bf51e5bb7edb52cfb37124516da4"}, 676 | {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:7ebe3416785f65c28f4f9441e916bfc8a54179c8dea73c23023f7086fa601c5d"}, 677 | {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:86c963186ca5e50d5c8287b1d1c9d3f8f024cbe343d048c5bd282aec2d8641f2"}, 678 | {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e0641b506486f0b4cd1500a2a65740243e8670a2549bb02bc4556a83af84ae03"}, 679 | {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71d72ca5eaaa8d38c8df16b7deb1a2da4f650c41b58bb142f3fb75d5ad4a611f"}, 680 | {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27e524624eace5c59af499cd97dc18bb201dc6a7a2da24bfc66ef151c69a5f2a"}, 681 | {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a3dde6cac75e0b0902778978d3b1646ca9f438654395a362cb21d9ad34b24acf"}, 682 | {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:00646784f6cd993b1e1c0e7b0fdcbccc375d539db95555477771c27555e3c556"}, 683 | {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:23598acb8ccaa3d1d875ef3b35cb6376535095e9405d91a3d57a8c7db5d29341"}, 684 | {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7f41533d7e3cf9520065f610b41ac1c76bc2161415955fbcead4981b22c7611e"}, 685 | {file = "pydantic_core-2.14.6.tar.gz", hash = "sha256:1fd0c1d395372843fba13a51c28e3bb9d59bd7aebfeb17358ffaaa1e4dbbe948"}, 686 | ] 687 | 688 | [package.dependencies] 689 | typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" 690 | 691 | [[package]] 692 | name = "typing-extensions" 693 | version = "4.10.0" 694 | description = "Backported and Experimental Type Hints for Python 3.8+" 695 | optional = false 696 | python-versions = ">=3.8" 697 | files = [ 698 | {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, 699 | {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, 700 | ] 701 | 702 | [[package]] 703 | name = "yarl" 704 | version = "1.9.4" 705 | description = "Yet another URL library" 706 | optional = false 707 | python-versions = ">=3.7" 708 | files = [ 709 | {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e"}, 710 | {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2"}, 711 | {file = "yarl-1.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66"}, 712 | {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234"}, 713 | {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392"}, 714 | {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551"}, 715 | {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455"}, 716 | {file = "yarl-1.9.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c"}, 717 | {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53"}, 718 | {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385"}, 719 | {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863"}, 720 | {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b"}, 721 | {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541"}, 722 | {file = "yarl-1.9.4-cp310-cp310-win32.whl", hash = "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d"}, 723 | {file = "yarl-1.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b"}, 724 | {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099"}, 725 | {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c"}, 726 | {file = "yarl-1.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0"}, 727 | {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525"}, 728 | {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8"}, 729 | {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9"}, 730 | {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42"}, 731 | {file = "yarl-1.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe"}, 732 | {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce"}, 733 | {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9"}, 734 | {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572"}, 735 | {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958"}, 736 | {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98"}, 737 | {file = "yarl-1.9.4-cp311-cp311-win32.whl", hash = "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31"}, 738 | {file = "yarl-1.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1"}, 739 | {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81"}, 740 | {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142"}, 741 | {file = "yarl-1.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074"}, 742 | {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129"}, 743 | {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2"}, 744 | {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78"}, 745 | {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4"}, 746 | {file = "yarl-1.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0"}, 747 | {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51"}, 748 | {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff"}, 749 | {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7"}, 750 | {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc"}, 751 | {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10"}, 752 | {file = "yarl-1.9.4-cp312-cp312-win32.whl", hash = "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7"}, 753 | {file = "yarl-1.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984"}, 754 | {file = "yarl-1.9.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f"}, 755 | {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17"}, 756 | {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14"}, 757 | {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5"}, 758 | {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd"}, 759 | {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7"}, 760 | {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e"}, 761 | {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec"}, 762 | {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c"}, 763 | {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead"}, 764 | {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434"}, 765 | {file = "yarl-1.9.4-cp37-cp37m-win32.whl", hash = "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749"}, 766 | {file = "yarl-1.9.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2"}, 767 | {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be"}, 768 | {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f"}, 769 | {file = "yarl-1.9.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf"}, 770 | {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1"}, 771 | {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57"}, 772 | {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa"}, 773 | {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130"}, 774 | {file = "yarl-1.9.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559"}, 775 | {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23"}, 776 | {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec"}, 777 | {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78"}, 778 | {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be"}, 779 | {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3"}, 780 | {file = "yarl-1.9.4-cp38-cp38-win32.whl", hash = "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece"}, 781 | {file = "yarl-1.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b"}, 782 | {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27"}, 783 | {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1"}, 784 | {file = "yarl-1.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91"}, 785 | {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b"}, 786 | {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5"}, 787 | {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34"}, 788 | {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136"}, 789 | {file = "yarl-1.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7"}, 790 | {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e"}, 791 | {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4"}, 792 | {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec"}, 793 | {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c"}, 794 | {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0"}, 795 | {file = "yarl-1.9.4-cp39-cp39-win32.whl", hash = "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575"}, 796 | {file = "yarl-1.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15"}, 797 | {file = "yarl-1.9.4-py3-none-any.whl", hash = "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad"}, 798 | {file = "yarl-1.9.4.tar.gz", hash = "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf"}, 799 | ] 800 | 801 | [package.dependencies] 802 | idna = ">=2.0" 803 | multidict = ">=4.0" 804 | 805 | [metadata] 806 | lock-version = "2.0" 807 | python-versions = "^3.11" 808 | content-hash = "beffd7c6d991581746049328837afe77094e10a783f86fa579e2d0317eb21c3c" 809 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | package-mode = false 3 | 4 | 5 | [tool.poetry.dependencies] 6 | python = "^3.11" 7 | aiogram = "^3.4.1" 8 | email-validator = "^2.1.1" 9 | 10 | 11 | [tool.poetry.group.dev.dependencies] 12 | black = "^24.3.0" 13 | 14 | [build-system] 15 | requires = ["poetry-core"] 16 | build-backend = "poetry.core.masonry.api" 17 | -------------------------------------------------------------------------------- /routers/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ("router",) 2 | 3 | from aiogram import Router 4 | 5 | from .admin_handlers import router as admin_router 6 | from .callback_handlers import router as callback_router 7 | from .commands import router as commands_router 8 | from .common import router as common_router 9 | from .media_handlers import router as media_router 10 | from .survey import router as survey_router 11 | 12 | router = Router(name=__name__) 13 | 14 | router.include_routers( 15 | callback_router, 16 | commands_router, 17 | survey_router, 18 | media_router, 19 | admin_router, 20 | ) 21 | 22 | # this one has to be the last! 23 | router.include_router(common_router) 24 | -------------------------------------------------------------------------------- /routers/admin_handlers.py: -------------------------------------------------------------------------------- 1 | from re import Match 2 | 3 | from aiogram import Router, F, types 4 | from magic_filter import RegexpMode 5 | 6 | from config import settings 7 | 8 | router = Router(name=__name__) 9 | 10 | 11 | @router.message(F.from_user.id.in_(settings.admin_ids), F.text == "secret") 12 | async def secret_admin_message(message: types.Message): 13 | await message.reply("Hi, admin!") 14 | 15 | 16 | @router.message( 17 | F.from_user.id.in_(settings.admin_ids), 18 | F.text.regexp(r"(\d+)", mode=RegexpMode.MATCH).as_("code"), 19 | ) 20 | async def handle_code(message: types.Message, code: Match[str]): 21 | await message.reply(f"Your code: {code.group()}") 22 | -------------------------------------------------------------------------------- /routers/callback_handlers/__init__.py: -------------------------------------------------------------------------------- 1 | from aiogram import Router 2 | 3 | from .actions_kb_callback_handlers import router as actions_kb_callback_router 4 | from .info_kb_callback_handlers import router as info_kb_callback_router 5 | from .shop_kb_callback_handlers import router as shop_kb_callback_router 6 | 7 | router = Router(name=__name__) 8 | 9 | router.include_routers( 10 | actions_kb_callback_router, 11 | info_kb_callback_router, 12 | shop_kb_callback_router, 13 | ) 14 | -------------------------------------------------------------------------------- /routers/callback_handlers/actions_kb_callback_handlers.py: -------------------------------------------------------------------------------- 1 | from random import randint 2 | 3 | from aiogram import F, Router 4 | from aiogram.types import CallbackQuery 5 | 6 | from keyboards.inline_keyboards.actions_kb import ( 7 | random_num_updated_cb_data, 8 | build_actions_kb, 9 | FixedRandomNumCbData, 10 | ) 11 | 12 | router = Router(name=__name__) 13 | 14 | 15 | @router.callback_query(F.data == random_num_updated_cb_data) 16 | async def handle_random_number_edited(callback_query: CallbackQuery): 17 | await callback_query.answer() 18 | await callback_query.message.edit_text( 19 | text=f"Random number: {randint(1, 100)}", 20 | reply_markup=build_actions_kb("Generate again"), 21 | ) 22 | 23 | 24 | @router.callback_query( 25 | FixedRandomNumCbData.filter(F.number == 66), 26 | ) 27 | async def handle_target_random_number_callback( 28 | callback_query: CallbackQuery, 29 | ): 30 | await callback_query.answer( 31 | text="Jackpot! 🎰", 32 | cache_time=30, 33 | ) 34 | 35 | 36 | @router.callback_query(FixedRandomNumCbData.filter()) 37 | async def handle_fixed_random_number_callback( 38 | callback_query: CallbackQuery, 39 | callback_data: FixedRandomNumCbData, 40 | ): 41 | await callback_query.answer( 42 | text=( 43 | f"Your fixed random number is {callback_data.number}\n" 44 | f"Callback data: {callback_query.data!r}" 45 | ), 46 | show_alert=True, 47 | cache_time=30, 48 | ) 49 | -------------------------------------------------------------------------------- /routers/callback_handlers/info_kb_callback_handlers.py: -------------------------------------------------------------------------------- 1 | from random import randint 2 | 3 | from aiogram import F, Router 4 | from aiogram.types import CallbackQuery 5 | 6 | from keyboards.inline_keyboards.info_kb import ( 7 | RandomNumAction, 8 | RandomNumCbData, 9 | ) 10 | 11 | router = Router(name=__name__) 12 | 13 | 14 | @router.callback_query( 15 | RandomNumCbData.filter(F.action == RandomNumAction.dice), 16 | ) 17 | async def handle_random_num_dice_cb(callback_query: CallbackQuery): 18 | await callback_query.answer( 19 | text=f"Your random dice: {randint(1, 21)}", 20 | cache_time=5, 21 | ) 22 | 23 | 24 | @router.callback_query( 25 | RandomNumCbData.filter(F.action == RandomNumAction.modal), 26 | ) 27 | async def handle_random_num_modal_cb(callback_query: CallbackQuery): 28 | await callback_query.answer( 29 | text=f"Random num: {randint(1, 100)}", 30 | cache_time=9, 31 | show_alert=True, 32 | ) 33 | -------------------------------------------------------------------------------- /routers/callback_handlers/shop_kb_callback_handlers.py: -------------------------------------------------------------------------------- 1 | from aiogram import Router, F 2 | from aiogram.types import CallbackQuery 3 | from aiogram.utils import markdown 4 | 5 | from keyboards.inline_keyboards.shop_kb import ( 6 | ShopCbData, 7 | ShopActions, 8 | build_shop_kb, 9 | build_products_kb, 10 | ProductCbData, 11 | ProductActions, 12 | product_details_kb, 13 | build_update_product_kb, 14 | ) 15 | 16 | router = Router(name=__name__) 17 | 18 | 19 | @router.callback_query( 20 | ShopCbData.filter(F.action == ShopActions.products), 21 | ) 22 | async def send_products_list(call: CallbackQuery): 23 | await call.answer() 24 | await call.message.edit_text( 25 | text="Available products:", 26 | reply_markup=build_products_kb(), 27 | ) 28 | 29 | 30 | @router.callback_query( 31 | ShopCbData.filter(F.action == ShopActions.root), 32 | ) 33 | async def handle_my_address_button(call: CallbackQuery): 34 | await call.answer() 35 | await call.message.edit_text( 36 | text="Your shop actions:", 37 | reply_markup=build_shop_kb(), 38 | ) 39 | 40 | 41 | @router.callback_query( 42 | ShopCbData.filter(F.action == ShopActions.address), 43 | ) 44 | async def handle_my_address_button(call: CallbackQuery): 45 | await call.answer( 46 | "Your address section is still in progress...", 47 | cache_time=30, 48 | ) 49 | 50 | 51 | @router.callback_query( 52 | ProductCbData.filter(F.action == ProductActions.details), 53 | ) 54 | async def handle_product_details_button( 55 | call: CallbackQuery, 56 | callback_data: ProductCbData, 57 | ): 58 | await call.answer() 59 | message_text = markdown.text( 60 | markdown.hbold(f"Product №{callback_data.id}"), 61 | markdown.text( 62 | markdown.hbold("Title:"), 63 | callback_data.title, 64 | ), 65 | markdown.text( 66 | markdown.hbold("Price:"), 67 | callback_data.price, 68 | ), 69 | sep="\n", 70 | ) 71 | await call.message.edit_text( 72 | text=message_text, 73 | reply_markup=product_details_kb(callback_data), 74 | ) 75 | 76 | 77 | @router.callback_query( 78 | ProductCbData.filter(F.action == ProductActions.update), 79 | ) 80 | async def handle_product_update_button( 81 | call: CallbackQuery, 82 | callback_data: ProductCbData, 83 | ): 84 | await call.answer() 85 | await call.message.edit_reply_markup( 86 | reply_markup=build_update_product_kb(callback_data), 87 | ) 88 | 89 | 90 | @router.callback_query( 91 | ProductCbData.filter(F.action == ProductActions.delete), 92 | ) 93 | async def handle_product_delete_button( 94 | call: CallbackQuery, 95 | ): 96 | await call.answer( 97 | text="Delete is still in progress...", 98 | ) 99 | -------------------------------------------------------------------------------- /routers/commands/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ("router",) 2 | 3 | from aiogram import Router 4 | 5 | from .base_commands import router as base_commands_router 6 | from .user_commands import router as user_commands_router 7 | 8 | router = Router(name=__name__) 9 | 10 | router.include_routers( 11 | base_commands_router, 12 | user_commands_router, 13 | ) 14 | -------------------------------------------------------------------------------- /routers/commands/base_commands.py: -------------------------------------------------------------------------------- 1 | from aiogram import F, Router, types 2 | from aiogram.enums import ParseMode 3 | from aiogram.filters import CommandStart, Command 4 | from aiogram.utils import markdown 5 | 6 | from keyboards.common_keyboards import ( 7 | ButtonText, 8 | get_on_start_kb, 9 | get_on_help_kb, 10 | get_actions_kb, 11 | ) 12 | from keyboards.inline_keyboards.info_kb import build_info_kb 13 | 14 | router = Router(name=__name__) 15 | 16 | 17 | @router.message(CommandStart()) 18 | async def handle_start(message: types.Message): 19 | url = "https://w7.pngwing.com/pngs/547/380/png-transparent-robot-waving-hand-bot-ai-robot-thumbnail.png" 20 | 21 | await message.answer( 22 | text=f"{markdown.hide_link(url)}Hello, {markdown.hbold(message.from_user.full_name)}!", 23 | parse_mode=ParseMode.HTML, 24 | reply_markup=get_on_start_kb(), 25 | ) 26 | 27 | 28 | @router.message(F.text == ButtonText.WHATS_NEXT) 29 | @router.message(Command("help", prefix="!/")) 30 | async def handle_help(message: types.Message): 31 | text = markdown.text( 32 | markdown.markdown_decoration.quote("I'm an {echo} bot."), 33 | markdown.text( 34 | "Send me", 35 | markdown.markdown_decoration.bold( 36 | markdown.text( 37 | markdown.underline("literally"), 38 | "any", 39 | ), 40 | ), 41 | markdown.markdown_decoration.quote("message!"), 42 | ), 43 | sep="\n", 44 | ) 45 | await message.answer( 46 | text=text, 47 | parse_mode=ParseMode.MARKDOWN_V2, 48 | reply_markup=get_on_help_kb(), 49 | ) 50 | 51 | 52 | @router.message(Command("more", prefix="!/")) 53 | async def handle_more(message: types.Message): 54 | markup = get_actions_kb() 55 | await message.answer( 56 | text="Choose action:", 57 | reply_markup=markup, 58 | ) 59 | 60 | 61 | @router.message(Command("info", prefix="!/")) 62 | async def handle_info_command(message: types.Message): 63 | markup = build_info_kb() 64 | await message.answer( 65 | text="Ссылки и прочие ресурсы:", 66 | reply_markup=markup, 67 | ) 68 | -------------------------------------------------------------------------------- /routers/commands/user_commands.py: -------------------------------------------------------------------------------- 1 | import csv 2 | import io 3 | 4 | import aiohttp 5 | from aiogram import Router, types 6 | from aiogram.enums import ParseMode, ChatAction 7 | from aiogram.filters import Command 8 | from aiogram.utils import markdown 9 | from aiogram.utils.chat_action import ChatActionSender 10 | 11 | from keyboards.inline_keyboards.actions_kb import build_actions_kb 12 | from keyboards.inline_keyboards.shop_kb import build_shop_kb 13 | 14 | router = Router(name=__name__) 15 | 16 | 17 | @router.message(Command("code", prefix="/!%")) 18 | async def handle_command_code(message: types.Message): 19 | text = markdown.text( 20 | "Here's Python code:", 21 | "", 22 | markdown.markdown_decoration.pre_language( 23 | # markdown.markdown_decoration.pre( 24 | markdown.text( 25 | "print('Hello world!')", 26 | "\n", 27 | "def foo():\n return 'bar'", 28 | sep="\n", 29 | ), 30 | language="python", 31 | ), 32 | "And here's some JS:", 33 | "", 34 | markdown.markdown_decoration.pre_language( 35 | markdown.text( 36 | "console.log('Hello world!')", 37 | "\n", 38 | "function foo() {\n return 'bar'\n}", 39 | sep="\n", 40 | ), 41 | language="javascript", 42 | ), 43 | sep="\n", 44 | ) 45 | await message.answer(text=text, parse_mode=ParseMode.MARKDOWN_V2) 46 | 47 | 48 | @router.message(Command("pic")) 49 | async def handle_command_pic(message: types.Message): 50 | await message.bot.send_chat_action( 51 | chat_id=message.chat.id, 52 | action=ChatAction.UPLOAD_PHOTO, 53 | ) 54 | url = "https://t4.ftcdn.net/jpg/00/97/58/97/360_F_97589769_t45CqXyzjz0KXwoBZT9PRaWGHRk5hQqQ.jpg" 55 | await message.reply_photo( 56 | photo=url, 57 | ) 58 | 59 | 60 | @router.message(Command("file")) 61 | async def handle_command_file(message: types.Message): 62 | await message.bot.send_chat_action( 63 | chat_id=message.chat.id, 64 | action=ChatAction.UPLOAD_DOCUMENT, 65 | ) 66 | file_path = "/Users/suren/Downloads/cat.jpeg" 67 | await message.reply_document( 68 | document=types.FSInputFile( 69 | path=file_path, 70 | filename="cat-big-photo.jpeg", 71 | ), 72 | ) 73 | 74 | 75 | @router.message(Command("text")) 76 | async def send_txt_file(message: types.Message): 77 | file = io.StringIO() 78 | file.write("Hello, world!\n") 79 | file.write("This is a text file.\n") 80 | await message.reply_document( 81 | document=types.BufferedInputFile( 82 | file=file.getvalue().encode("utf-8"), 83 | filename="text.txt", 84 | ), 85 | ) 86 | 87 | 88 | @router.message(Command("csv")) 89 | async def send_csv_file(message: types.Message): 90 | await message.bot.send_chat_action( 91 | chat_id=message.chat.id, 92 | action=ChatAction.TYPING, 93 | ) 94 | file = io.StringIO() 95 | csv_writer = csv.writer(file) 96 | csv_writer.writerows( 97 | [ 98 | ["Name", "Age", "City"], 99 | ["John Smith", "28", "New York"], 100 | ["Jane Doe", "32", "Los Angeles"], 101 | ["Mike Johnson", "40", "Chicago"], 102 | ] 103 | ) 104 | await message.reply_document( 105 | document=types.BufferedInputFile( 106 | file=file.getvalue().encode("utf-8"), 107 | filename="people.csv", 108 | ), 109 | ) 110 | 111 | 112 | async def send_big_file(message: types.Message): 113 | file = io.BytesIO() 114 | url = "https://images.unsplash.com/photo-1608848461950-0fe51dfc41cb" 115 | async with aiohttp.ClientSession() as session: 116 | async with session.get(url) as response: 117 | result_bytes = await response.read() 118 | 119 | file.write(result_bytes) 120 | await message.reply_document( 121 | document=types.BufferedInputFile( 122 | file=file.getvalue(), 123 | filename="cat-big-pic.jpeg", 124 | ), 125 | ) 126 | 127 | 128 | @router.message(Command("pic_file")) 129 | async def send_pic_file_buffered(message: types.Message): 130 | await message.bot.send_chat_action( 131 | chat_id=message.chat.id, 132 | action=ChatAction.UPLOAD_DOCUMENT, 133 | ) 134 | async with ChatActionSender.upload_document( 135 | bot=message.bot, 136 | chat_id=message.chat.id, 137 | ): 138 | await send_big_file(message) 139 | 140 | 141 | @router.message(Command("actions", prefix="!/")) 142 | async def send_actions_message_w_kb(message: types.Message): 143 | await message.answer( 144 | text="Your actions:", 145 | reply_markup=build_actions_kb(), 146 | ) 147 | 148 | 149 | @router.message(Command("shop", prefix="!/")) 150 | async def send_shop_message_kb(message: types.Message): 151 | await message.answer( 152 | text="Your shop actions:", 153 | reply_markup=build_shop_kb(), 154 | ) 155 | -------------------------------------------------------------------------------- /routers/common.py: -------------------------------------------------------------------------------- 1 | from aiogram import F, Router, types 2 | from aiogram.enums import ChatAction 3 | from aiogram.filters import Command 4 | from aiogram.fsm.context import FSMContext 5 | from aiogram.types import ReplyKeyboardRemove 6 | 7 | from keyboards.common_keyboards import ButtonText 8 | 9 | router = Router(name=__name__) 10 | 11 | 12 | @router.message(F.text == ButtonText.BYE) 13 | async def handle_bye_message(message: types.Message): 14 | await message.answer( 15 | text="See you later! Click /start any time!", 16 | reply_markup=ReplyKeyboardRemove(), 17 | ) 18 | 19 | 20 | @router.message(Command("cancel")) 21 | @router.message(F.text.casefold() == "cancel") 22 | async def cancel_handler(message: types.Message, state: FSMContext) -> None: 23 | """ 24 | Allow user to cancel any action 25 | """ 26 | current_state = await state.get_state() 27 | if current_state is None: 28 | await message.reply(text="OK, but nothing was going on.") 29 | return 30 | 31 | await state.clear() 32 | await message.answer( 33 | f"Cancelled state {current_state}.", 34 | reply_markup=ReplyKeyboardRemove(), 35 | ) 36 | 37 | 38 | @router.message() 39 | async def echo_message(message: types.Message): 40 | if message.poll: 41 | await message.forward(chat_id=message.chat.id) 42 | return 43 | await message.answer( 44 | text="Wait a second...", 45 | parse_mode=None, 46 | ) 47 | if message.sticker: 48 | await message.bot.send_chat_action( 49 | chat_id=message.chat.id, 50 | action=ChatAction.CHOOSE_STICKER, 51 | ) 52 | try: 53 | await message.copy_to(chat_id=message.chat.id) 54 | except TypeError: 55 | await message.reply(text="Something new 🙂") 56 | -------------------------------------------------------------------------------- /routers/media_handlers.py: -------------------------------------------------------------------------------- 1 | from aiogram import Router, F, types 2 | 3 | router = Router(name=__name__) 4 | 5 | any_media_filter = F.photo | F.video | F.document 6 | 7 | 8 | @router.message(F.photo, ~F.caption) 9 | async def handle_photo_wo_caption(message: types.Message): 10 | caption = "I can't see, sorry. Could you describe it please?" 11 | await message.reply_photo( 12 | photo=message.photo[-1].file_id, 13 | caption=caption, 14 | ) 15 | 16 | 17 | @router.message(F.photo, F.caption.contains("please")) 18 | async def handle_photo_with_please_caption(message: types.Message): 19 | await message.reply("Don't beg me. I can't see, sorry.") 20 | 21 | 22 | @router.message(any_media_filter, ~F.caption) 23 | async def handle_any_media_wo_caption(message: types.Message): 24 | if message.document: 25 | await message.reply_document( 26 | document=message.document.file_id, 27 | ) 28 | elif message.video: 29 | await message.reply_video( 30 | video=message.video.file_id, 31 | ) 32 | else: 33 | await message.reply("I can't see.") 34 | 35 | 36 | @router.message(any_media_filter, F.caption) 37 | async def handle_any_media_w_caption(message: types.Message): 38 | await message.reply(f"Smth is on media. Your text: {message.caption!r}") 39 | -------------------------------------------------------------------------------- /routers/survey/__init__.py: -------------------------------------------------------------------------------- 1 | from aiogram import Router 2 | 3 | from .handlers import router as handlers_router 4 | 5 | router = Router(name="survey") 6 | router.include_router(handlers_router) 7 | -------------------------------------------------------------------------------- /routers/survey/handlers.py: -------------------------------------------------------------------------------- 1 | from aiogram import Router, types, F 2 | from aiogram.filters import Command, StateFilter 3 | from aiogram.fsm.context import FSMContext 4 | from aiogram.fsm.state import default_state 5 | 6 | from .survey_handlers.email_newsletter_handlers import ( 7 | router as email_newsletter_router, 8 | ) 9 | from .survey_handlers.user_email_handlers import ( 10 | router as user_email_router, 11 | ) 12 | from .survey_handlers.full_name import router as full_name_router 13 | from .survey_handlers.select_sport_handlers import router as select_sport_router 14 | 15 | from .states import Survey, SurveySportDetails 16 | 17 | router = Router(name=__name__) 18 | router.include_router(full_name_router) 19 | router.include_router(user_email_router) 20 | router.include_router(select_sport_router) 21 | router.include_router(email_newsletter_router) 22 | 23 | 24 | @router.message( 25 | Command("survey", prefix="!/"), 26 | default_state, 27 | ) 28 | async def handle_start_survey(message: types.Message, state: FSMContext): 29 | await state.set_state(Survey.full_name) 30 | await message.answer( 31 | "Welcome to our weekly survey! What's your name?", 32 | reply_markup=types.ReplyKeyboardRemove(), 33 | ) 34 | 35 | 36 | survey_states = StateFilter( 37 | Survey(), 38 | SurveySportDetails(), 39 | ) 40 | 41 | 42 | @router.message(Command("cancel"), survey_states) 43 | @router.message(F.text.casefold() == "cancel", survey_states) 44 | async def cancel_handler(message: types.Message, state: FSMContext) -> None: 45 | """ 46 | Allow user to cancel survey 47 | """ 48 | current_state = await state.get_state() 49 | await state.clear() 50 | await message.answer( 51 | f"Cancelled survey on step {current_state}. Start again: /survey", 52 | reply_markup=types.ReplyKeyboardRemove(), 53 | ) 54 | -------------------------------------------------------------------------------- /routers/survey/states.py: -------------------------------------------------------------------------------- 1 | from enum import StrEnum 2 | 3 | from aiogram.fsm.state import StatesGroup, State 4 | 5 | 6 | class Survey(StatesGroup): 7 | full_name = State() 8 | email = State() 9 | sport = State() 10 | email_newsletter = State() 11 | 12 | 13 | class SurveySportDetails(StatesGroup): 14 | tennis = State() 15 | football = State() 16 | formula_one = State() 17 | 18 | 19 | class KnownSports(StrEnum): 20 | tennis = "Tennis" 21 | football = "Football" 22 | formula_one = "Formula One" 23 | 24 | 25 | class KnownF1Tracks(StrEnum): 26 | monaco = "Monaco" 27 | spa = "Spa" 28 | suzuka = "Suzuka" 29 | monza = "Monza" 30 | -------------------------------------------------------------------------------- /routers/survey/survey_handlers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mahenzon/demo-tg-bot/e4b687d51600722f1f93b26fdc17d329bad06ee1/routers/survey/survey_handlers/__init__.py -------------------------------------------------------------------------------- /routers/survey/survey_handlers/email_newsletter_handlers.py: -------------------------------------------------------------------------------- 1 | from aiogram import Router, F, types 2 | from aiogram.fsm.context import FSMContext 3 | from aiogram.utils import markdown 4 | 5 | from keyboards.common_keyboards import build_yes_or_no_keyboard 6 | from routers.survey.states import Survey 7 | 8 | router = Router(name=__name__) 9 | 10 | 11 | async def send_survey_results(message: types.Message, data: dict) -> None: 12 | text = markdown.text( 13 | markdown.hunderline("Your survey results:"), 14 | "", 15 | markdown.text("Name:", markdown.hbold(data["full_name"])), 16 | markdown.text("Email:", markdown.hcode(data["email"])), 17 | "", 18 | markdown.text("Preferred sport:", markdown.hbold(data["sport"])), 19 | markdown.text("Q:", markdown.hitalic(data["sport_question"])), 20 | markdown.text("A:", markdown.hitalic(data["sport_answer"])), 21 | "", 22 | ( 23 | "Cool, we'll send you our news!" 24 | if data["newsletter_ok"] 25 | else "And we won't bother you again." 26 | ), 27 | sep="\n", 28 | ) 29 | await message.answer( 30 | text=text, 31 | reply_markup=types.ReplyKeyboardRemove(), 32 | ) 33 | 34 | 35 | @router.message(Survey.email_newsletter, F.text.casefold() == "yes") 36 | async def handle_survey_email_newsletter_ok( 37 | message: types.Message, 38 | state: FSMContext, 39 | ): 40 | data = await state.update_data(newsletter_ok=True) 41 | await state.clear() 42 | await send_survey_results(message, data) 43 | 44 | 45 | @router.message(Survey.email_newsletter, F.text.casefold() == "no") 46 | async def handle_survey_email_newsletter_not_ok( 47 | message: types.Message, 48 | state: FSMContext, 49 | ): 50 | data = await state.update_data(newsletter_ok=False) 51 | await state.clear() 52 | await send_survey_results(message, data) 53 | 54 | 55 | @router.message(Survey.email_newsletter) 56 | async def handle_survey_email_newsletter_could_not_understand(message: types.Message): 57 | await message.answer( 58 | text=( 59 | "Sorry, I didn't understand, " 60 | f"please send {markdown.hcode('yes')} or {markdown.hcode('no')}" 61 | ), 62 | reply_markup=build_yes_or_no_keyboard(), 63 | ) 64 | -------------------------------------------------------------------------------- /routers/survey/survey_handlers/full_name.py: -------------------------------------------------------------------------------- 1 | from aiogram import Router, F, types 2 | from aiogram.enums import ParseMode 3 | from aiogram.fsm.context import FSMContext 4 | from aiogram.utils import markdown 5 | 6 | from routers.survey.states import Survey 7 | 8 | router = Router(name=__name__) 9 | 10 | 11 | @router.message(Survey.full_name, F.text) 12 | async def handle_survey_user_full_name(message: types.Message, state: FSMContext): 13 | await state.update_data(full_name=message.text) 14 | await state.set_state(Survey.email) 15 | await message.answer( 16 | f"Hello, {markdown.hbold(message.text)}, now please share your email", 17 | parse_mode=ParseMode.HTML, 18 | ) 19 | 20 | 21 | @router.message(Survey.full_name) 22 | async def handle_survey_user_full_name_invalid_content_type(message: types.Message): 23 | await message.answer( 24 | "Sorry, I didn't understand, send your full name as text. /cancel ?", 25 | ) 26 | -------------------------------------------------------------------------------- /routers/survey/survey_handlers/select_sport_handlers.py: -------------------------------------------------------------------------------- 1 | from aiogram import Router, types, F 2 | from aiogram.filters import StateFilter 3 | from aiogram.fsm.context import FSMContext 4 | from aiogram.fsm.state import State 5 | 6 | from keyboards.common_keyboards import build_select_keyboard, build_yes_or_no_keyboard 7 | from routers.survey.states import ( 8 | Survey, 9 | SurveySportDetails, 10 | KnownSports, 11 | KnownF1Tracks, 12 | ) 13 | 14 | router = Router(name=__name__) 15 | 16 | 17 | known_sport_to_next: dict[KnownSports | str, tuple[State, str]] = { 18 | KnownSports.tennis: ( 19 | SurveySportDetails.tennis, 20 | "Who is your favourite tennis player?", 21 | ), 22 | KnownSports.football: ( 23 | SurveySportDetails.football, 24 | "What is your favourite football team?", 25 | ), 26 | KnownSports.formula_one: ( 27 | SurveySportDetails.formula_one, 28 | "What is your favourite formula track?", 29 | ), 30 | } 31 | 32 | known_f1_tracks_kb = build_select_keyboard(KnownF1Tracks) 33 | known_sport_to_kb: dict = { 34 | KnownSports.formula_one: known_f1_tracks_kb, 35 | } 36 | 37 | 38 | @router.message( 39 | Survey.sport, 40 | F.text.cast(KnownSports), 41 | ) 42 | async def select_sport(message: types.Message, state: FSMContext): 43 | next_state, question_text = known_sport_to_next[message.text] 44 | await state.update_data( 45 | sport=message.text, 46 | sport_question=question_text, 47 | ) 48 | await state.set_state(next_state) 49 | kb = types.ReplyKeyboardRemove() 50 | if message.text in known_sport_to_kb: 51 | kb = known_sport_to_kb[message.text] 52 | await message.answer( 53 | text=question_text, 54 | reply_markup=kb, 55 | ) 56 | 57 | 58 | @router.message(Survey.sport) 59 | async def select_sport_invalid_choice(message: types.Message): 60 | await message.answer( 61 | "Unknown sport, please select one of the following:", 62 | reply_markup=build_select_keyboard(KnownSports), 63 | ) 64 | 65 | 66 | @router.message( 67 | F.text, 68 | StateFilter( 69 | SurveySportDetails.tennis, 70 | SurveySportDetails.football, 71 | ), 72 | ) 73 | @router.message( 74 | F.text.cast(KnownF1Tracks), 75 | SurveySportDetails.formula_one, 76 | ) 77 | async def handle_selected_sport_details_option( 78 | message: types.Message, 79 | state: FSMContext, 80 | ): 81 | await state.update_data(sport_answer=message.text) 82 | await state.set_state(Survey.email_newsletter) 83 | await message.answer( 84 | text=( 85 | "Would you like to be notified about this sport? Email newsletter.\n" 86 | "This is last step, but you can /cancel any time." 87 | ), 88 | reply_markup=build_yes_or_no_keyboard(), 89 | ) 90 | 91 | 92 | @router.message(SurveySportDetails.tennis) 93 | async def handle_tennis_player_not_text(message: types.Message): 94 | await message.answer(text="Please name tennis player using text.") 95 | 96 | 97 | @router.message(SurveySportDetails.football) 98 | async def handle_football_team_not_text(message: types.Message): 99 | await message.answer(text="Please name football team using text.") 100 | 101 | 102 | @router.message(SurveySportDetails.formula_one) 103 | async def handle_formula_one_not_one_of_tracks(message: types.Message): 104 | await message.answer( 105 | text="Please select one of known F1 tracks:", 106 | reply_markup=known_f1_tracks_kb, 107 | ) 108 | -------------------------------------------------------------------------------- /routers/survey/survey_handlers/user_email_handlers.py: -------------------------------------------------------------------------------- 1 | from aiogram import Router, F, types 2 | from aiogram.fsm.context import FSMContext 3 | from aiogram.utils import markdown 4 | from email_validator import validate_email 5 | 6 | from keyboards.common_keyboards import build_select_keyboard 7 | from routers.survey.states import Survey, KnownSports 8 | 9 | # from validators.email_validators import ( 10 | # valid_email_filter, 11 | # valid_email_message_text, 12 | # valid_email, 13 | # ) 14 | 15 | router = Router(name=__name__) 16 | 17 | 18 | @router.message( 19 | Survey.email, 20 | # valid_email_filter, 21 | # F.func(valid_email_message_text).as_("email"), 22 | # F.text.cast(valid_email).as_("email"), 23 | F.text.cast(validate_email).normalized.as_("email"), 24 | ) 25 | async def handle_survey_email_message( 26 | message: types.Message, 27 | state: FSMContext, 28 | email: str, 29 | ): 30 | await state.update_data(email=email) 31 | await state.set_state(Survey.sport) 32 | await message.answer( 33 | text=( 34 | f"Cool, your email is now {markdown.hcode(email)}.\n" 35 | "Which sport would you prefer?" 36 | ), 37 | reply_markup=build_select_keyboard(KnownSports), 38 | ) 39 | 40 | 41 | @router.message(Survey.email) 42 | async def handle_survey_invalid_email_message(message: types.Message): 43 | await message.answer( 44 | text="Invalid email, please try again. Cancel survey? Tap /cancel", 45 | ) 46 | -------------------------------------------------------------------------------- /validators/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mahenzon/demo-tg-bot/e4b687d51600722f1f93b26fdc17d329bad06ee1/validators/__init__.py -------------------------------------------------------------------------------- /validators/email_validators.py: -------------------------------------------------------------------------------- 1 | from aiogram import types 2 | 3 | from email_validator import validate_email, EmailNotValidError 4 | 5 | 6 | def valid_email_filter(message: types.Message) -> dict[str, str] | None: 7 | try: 8 | email = validate_email(message.text) 9 | except EmailNotValidError: 10 | return None 11 | 12 | return {"email": email.normalized} 13 | 14 | 15 | def valid_email(text: str) -> str | None: 16 | try: 17 | email = validate_email(text) 18 | except EmailNotValidError: 19 | return None 20 | 21 | return email.normalized 22 | 23 | 24 | def valid_email_message_text(message: types.Message) -> str | None: 25 | return valid_email(message.text) 26 | --------------------------------------------------------------------------------