From de7fe059b856baa790f0ff3d8cf217512ce24d56 Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Wed, 6 Nov 2024 11:50:21 +0200 Subject: [PATCH] V1 (#106) --- .gitignore | 1 + .prettierignore | 14 + .prettierrc | 12 + Makefile | 47 + README.md | 22 +- __init__.py | 19 +- config.json | 2 +- crud.py | 838 +++--- description.md | 6 +- helpers.py | 11 +- migrations.py | 43 +- models.py | 197 +- nostr/event.py | 4 +- nostr/nostr_client.py | 12 +- package.json | 15 + poetry.lock | 2616 +++++++++++++++++ pyproject.toml | 96 + services.py | 105 +- static/components/direct-messages.js | 170 ++ .../direct-messages/direct-messages.html | 104 - .../direct-messages/direct-messages.js | 173 -- static/components/key-pair.js | 22 + static/components/key-pair/key-pair.js | 25 - static/components/merchant-details.js | 102 + .../merchant-details/merchant-details.js | 106 - static/components/order-list.js | 406 +++ static/components/order-list/order-list.html | 212 -- static/components/order-list/order-list.js | 409 --- static/components/shipping-zones.js | 183 ++ .../shipping-zones/shipping-zones.js | 186 -- static/components/stall-details.js | 338 +++ .../stall-details/stall-details.html | 255 -- .../components/stall-details/stall-details.js | 334 --- static/components/stall-list.js | 262 ++ static/components/stall-list/stall-list.html | 117 - static/components/stall-list/stall-list.js | 266 -- static/js/index.js | 443 ++- static/js/utils.js | 6 +- static/market/index.html | 10 +- tasks.py | 5 +- .../components/direct-messages.html | 169 ++ .../nostrmarket/components}/key-pair.html | 10 +- .../components}/merchant-details.html | 0 .../nostrmarket/components/order-list.html | 369 +++ .../components}/shipping-zones.html | 11 +- .../nostrmarket/components/stall-details.html | 466 +++ .../nostrmarket/components/stall-list.html | 215 ++ templates/nostrmarket/index.html | 56 +- templates/nostrmarket/market.html | 73 +- toc.md | 9 +- views.py | 11 +- views_api.py | 211 +- 52 files changed, 6679 insertions(+), 3115 deletions(-) create mode 100644 .prettierignore create mode 100644 .prettierrc create mode 100644 Makefile create mode 100644 package.json create mode 100644 poetry.lock create mode 100644 pyproject.toml create mode 100644 static/components/direct-messages.js delete mode 100644 static/components/direct-messages/direct-messages.html delete mode 100644 static/components/direct-messages/direct-messages.js create mode 100644 static/components/key-pair.js delete mode 100644 static/components/key-pair/key-pair.js create mode 100644 static/components/merchant-details.js delete mode 100644 static/components/merchant-details/merchant-details.js create mode 100644 static/components/order-list.js delete mode 100644 static/components/order-list/order-list.html delete mode 100644 static/components/order-list/order-list.js create mode 100644 static/components/shipping-zones.js delete mode 100644 static/components/shipping-zones/shipping-zones.js create mode 100644 static/components/stall-details.js delete mode 100644 static/components/stall-details/stall-details.html delete mode 100644 static/components/stall-details/stall-details.js create mode 100644 static/components/stall-list.js delete mode 100644 static/components/stall-list/stall-list.html delete mode 100644 static/components/stall-list/stall-list.js create mode 100644 templates/nostrmarket/components/direct-messages.html rename {static/components/key-pair => templates/nostrmarket/components}/key-pair.html (84%) rename {static/components/merchant-details => templates/nostrmarket/components}/merchant-details.html (100%) create mode 100644 templates/nostrmarket/components/order-list.html rename {static/components/shipping-zones => templates/nostrmarket/components}/shipping-zones.html (91%) create mode 100644 templates/nostrmarket/components/stall-details.html create mode 100644 templates/nostrmarket/components/stall-list.html diff --git a/.gitignore b/.gitignore index 10a11d5..f3a8853 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ __pycache__ htmlcov test-reports tests/data/*.sqlite3 +node_modules *.swo *.swp diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..931dc7f --- /dev/null +++ b/.prettierignore @@ -0,0 +1,14 @@ +**/.git +**/.svn +**/.hg +**/node_modules + +*.yml + +**/static/market/* +**/static/js/nostr.bundle.js* + + +flake.lock + +.venv diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..725c398 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,12 @@ +{ + "semi": false, + "arrowParens": "avoid", + "insertPragma": false, + "printWidth": 80, + "proseWrap": "preserve", + "singleQuote": true, + "trailingComma": "none", + "useTabs": false, + "bracketSameLine": false, + "bracketSpacing": false +} diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9a08146 --- /dev/null +++ b/Makefile @@ -0,0 +1,47 @@ +all: format check + +format: prettier black ruff + +check: mypy pyright checkblack checkruff checkprettier + +prettier: + poetry run ./node_modules/.bin/prettier --write . +pyright: + poetry run ./node_modules/.bin/pyright + +mypy: + poetry run mypy . + +black: + poetry run black . + +ruff: + poetry run ruff check . --fix + +checkruff: + poetry run ruff check . + +checkprettier: + poetry run ./node_modules/.bin/prettier --check . + +checkblack: + poetry run black --check . + +checkeditorconfig: + editorconfig-checker + +test: + PYTHONUNBUFFERED=1 \ + DEBUG=true \ + poetry run pytest +install-pre-commit-hook: + @echo "Installing pre-commit hook to git" + @echo "Uninstall the hook with poetry run pre-commit uninstall" + poetry run pre-commit install + +pre-commit: + poetry run pre-commit run --all-files + + +checkbundle: + @echo "skipping checkbundle" diff --git a/README.md b/README.md index 9e3f157..1839351 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Nostr Market ([NIP-15](https://github.com/nostr-protocol/nips/blob/master/15.md)) - [LNbits](https://github.com/lnbits/lnbits) extension -For more about LNBits extension check [this tutorial](https://github.com/lnbits/lnbits/wiki/LNbits-Extensions). +For more about LNBits extension check [this tutorial](https://github.com/lnbits/lnbits/wiki/LNbits-Extensions). **Demo at Nostrica here**. @@ -8,15 +8,15 @@ > The concepts around resilience in Diagon Alley helped influence the creation of the NOSTR protocol, now we get to build Diagon Alley on NOSTR! - ## Prerequisites + This extension uses the LNbits [nostrclient](https://github.com/lnbits/nostrclient) extension, an extension that makes _nostrfying_ other extensions easy. ![image](https://user-images.githubusercontent.com/2951406/236773044-81d3f30b-1ce7-4c5d-bdaf-b4a80ddddc58.png) + - before you continue, please make sure that [nostrclient](https://github.com/lnbits/nostrclient) extension is installed, activated and correctly configured. - [nostrclient](https://github.com/lnbits/nostrclient) is usually installed as admin-only extension, so if you do not have admin access please ask an admin to confirm that [nostrclient](https://github.com/lnbits/nostrclient) is OK. - see the [Troubleshoot](https://github.com/lnbits/nostrclient#troubleshoot) section for more details on how to check the health of `nostrclient` extension - ## Create, or import, a merchant account As a merchant you need to provide a Nostr key pair, or the extension can generate one for you. @@ -97,35 +97,39 @@ Make sure to add your `merchant` public key to the list: ![image](https://user-images.githubusercontent.com/2951406/236787686-0e300c0a-eb5d-4490-aa70-568738ac78f4.png) ### Styling + In order to create a customized Marketplace, we use `naddr` as defined in [NIP-19](https://github.com/nostr-protocol/nips/blob/master/19.md#shareable-identifiers-with-extra-metadata). You must create an event (kind: `30019`) that has all the custom properties, including merchants and relays, of your marketplace. Start by going to the marketplace page: ![vanilla market](https://i.imgur.com/nCaMh1N.png) -You'll need to Login, and head over to *Marketplace Info*. Optionally import some merchants and relays, that will be included in the event. Click on *Edit* and fill out your marketplace custom info: +You'll need to Login, and head over to _Marketplace Info_. Optionally import some merchants and relays, that will be included in the event. Click on _Edit_ and fill out your marketplace custom info: ![edit](https://i.imgur.com/wEuHuN9.png) Fill in the optional fields: + - Add a name to the Marketplace - Add a small description - Add a logo image URL - Add a banner image URL (max height: 250px) -- Choose a theme +- Choose a theme -By clicking *Publish*, a `kind: 30019` event will be sent to the defined relays containing all the information about your custom Marketplace. On the left drawer, a button with *Copy Naddr* will show up. +By clicking _Publish_, a `kind: 30019` event will be sent to the defined relays containing all the information about your custom Marketplace. On the left drawer, a button with _Copy Naddr_ will show up. ![copy naddr](https://i.imgur.com/VuNIMVf.png) -You can then share your Marketplace, with the merchants and relays, banner, and style by using that Nostr identifier. The URL for the marketplace will be for example: `https://legend.lnbits.com/nostrmarket/market?naddr=naddr1qqfy6ctjddjhgurvv93k....`, you need to include the URL parameter `naddr=`. When a user visits that URL, the client will get the `30019` event and configure the Marketplace to what you defined. In the example bellow, a couple of merchants, relays, `autumn` theme, name (*Veggies Market*) and a header banner: +You can then share your Marketplace, with the merchants and relays, banner, and style by using that Nostr identifier. The URL for the marketplace will be for example: `https://legend.lnbits.com/nostrmarket/market?naddr=naddr1qqfy6ctjddjhgurvv93k....`, you need to include the URL parameter `naddr=`. When a user visits that URL, the client will get the `30019` event and configure the Marketplace to what you defined. In the example bellow, a couple of merchants, relays, `autumn` theme, name (_Veggies Market_) and a header banner: ![final](https://i.imgur.com/EYG7vYS.png) The nostr event is a replaceable event, so you can change it to what you like and publish a new one to replace a previous one. For example adding a new merchant, or remove, change theme, add more relays,e tc... - ## Troubleshoot + ### Check communication with Nostr + In order to test that the integration with Nostr is working fine, one can add an `npub` to the chat box and check that DMs are working as expected: https://user-images.githubusercontent.com/2951406/236777983-259f81d8-136f-48b3-bb73-80749819b5f9.mov ### Restart connection to Nostr + If the communication with Nostr is not working then an admin user can `Restart` the Nostr connection. Merchants can afterwards re-publish their products. @@ -133,8 +137,8 @@ Merchants can afterwards re-publish their products. https://user-images.githubusercontent.com/2951406/236778651-7ada9f6d-07a1-491c-ac9c-55530326c32a.mp4 ### Check Nostrclient extension -- see the [Troubleshoot](https://github.com/lnbits/nostrclient#troubleshoot) section for more details on how to check the health of `nostrclient` extension +- see the [Troubleshoot](https://github.com/lnbits/nostrclient#troubleshoot) section for more details on how to check the health of `nostrclient` extension ## Aditional info diff --git a/__init__.py b/__init__.py index 3d90cee..921c383 100644 --- a/__init__.py +++ b/__init__.py @@ -1,11 +1,10 @@ import asyncio from fastapi import APIRouter -from loguru import logger - from lnbits.db import Database from lnbits.helpers import template_renderer from lnbits.tasks import create_permanent_unique_task +from loguru import logger from .nostr.nostr_client import NostrClient @@ -24,14 +23,14 @@ def nostrmarket_renderer(): return template_renderer(["nostrmarket/templates"]) + nostr_client: NostrClient = NostrClient() -from .tasks import wait_for_nostr_events, wait_for_paid_invoices +from .tasks import wait_for_nostr_events, wait_for_paid_invoices # noqa from .views import * # noqa from .views_api import * # noqa - scheduled_tasks: list[asyncio.Task] = [] @@ -57,7 +56,13 @@ async def _wait_for_nostr_events(): await asyncio.sleep(15) await wait_for_nostr_events(nostr_client) - task1 = create_permanent_unique_task("ext_nostrmarket_paid_invoices", wait_for_paid_invoices) - task2 = create_permanent_unique_task("ext_nostrmarket_subscribe_to_nostr_client", _subscribe_to_nostr_client) - task3 = create_permanent_unique_task("ext_nostrmarket_wait_for_events", _wait_for_nostr_events) + task1 = create_permanent_unique_task( + "ext_nostrmarket_paid_invoices", wait_for_paid_invoices + ) + task2 = create_permanent_unique_task( + "ext_nostrmarket_subscribe_to_nostr_client", _subscribe_to_nostr_client + ) + task3 = create_permanent_unique_task( + "ext_nostrmarket_wait_for_events", _wait_for_nostr_events + ) scheduled_tasks.extend([task1, task2, task3]) diff --git a/config.json b/config.json index 5fa6879..fe7456f 100644 --- a/config.json +++ b/config.json @@ -2,7 +2,7 @@ "name": "Nostr Market", "short_description": "Nostr Webshop/market on LNbits", "tile": "/nostrmarket/static/images/bitcoin-shop.png", - "min_lnbits_version": "0.12.6", + "min_lnbits_version": "1.0.0", "contributors": [ { "name": "motorina0", diff --git a/crud.py b/crud.py index e8ab117..4cb18c2 100644 --- a/crud.py +++ b/crud.py @@ -1,5 +1,5 @@ import json -from typing import List, Optional +from typing import List, Optional, Tuple from lnbits.helpers import urlsafe_short_hash @@ -13,25 +13,29 @@ Order, PartialDirectMessage, PartialMerchant, - PartialProduct, - PartialStall, - PartialZone, Product, Stall, Zone, ) -######################################## MERCHANT ######################################## +######################################## MERCHANT ###################################### async def create_merchant(user_id: str, m: PartialMerchant) -> Merchant: merchant_id = urlsafe_short_hash() await db.execute( """ - INSERT INTO nostrmarket.merchants (user_id, id, private_key, public_key, meta) - VALUES (?, ?, ?, ?, ?) + INSERT INTO nostrmarket.merchants + (user_id, id, private_key, public_key, meta) + VALUES (:user_id, :id, :private_key, :public_key, :meta) """, - (user_id, merchant_id, m.private_key, m.public_key, json.dumps(dict(m.config))), + { + "user_id": user_id, + "id": merchant_id, + "private_key": m.private_key, + "public_key": m.public_key, + "meta": json.dumps(dict(m.config)), + }, ) merchant = await get_merchant(user_id, merchant_id) assert merchant, "Created merchant cannot be retrieved" @@ -43,10 +47,10 @@ async def update_merchant( ) -> Optional[Merchant]: await db.execute( f""" - UPDATE nostrmarket.merchants SET meta = ?, time = {db.timestamp_now} - WHERE id = ? AND user_id = ? + UPDATE nostrmarket.merchants SET meta = :meta, time = {db.timestamp_now} + WHERE id = :id AND user_id = :user_id """, - (json.dumps(config.dict()), merchant_id, user_id), + {"meta": json.dumps(config.dict()), "id": merchant_id, "user_id": user_id}, ) return await get_merchant(user_id, merchant_id) @@ -55,46 +59,46 @@ async def touch_merchant(user_id: str, merchant_id: str) -> Optional[Merchant]: await db.execute( f""" UPDATE nostrmarket.merchants SET time = {db.timestamp_now} - WHERE id = ? AND user_id = ? + WHERE id = :id AND user_id = :user_id """, - (merchant_id, user_id), + {"id": merchant_id, "user_id": user_id}, ) return await get_merchant(user_id, merchant_id) async def get_merchant(user_id: str, merchant_id: str) -> Optional[Merchant]: - row = await db.fetchone( - """SELECT * FROM nostrmarket.merchants WHERE user_id = ? AND id = ?""", - ( - user_id, - merchant_id, - ), + row: dict = await db.fetchone( + """SELECT * FROM nostrmarket.merchants WHERE user_id = :user_id AND id = :id""", + { + "user_id": user_id, + "id": merchant_id, + }, ) return Merchant.from_row(row) if row else None async def get_merchant_by_pubkey(public_key: str) -> Optional[Merchant]: - row = await db.fetchone( - """SELECT * FROM nostrmarket.merchants WHERE public_key = ? """, - (public_key,), + row: dict = await db.fetchone( + """SELECT * FROM nostrmarket.merchants WHERE public_key = :public_key""", + {"public_key": public_key}, ) return Merchant.from_row(row) if row else None -async def get_merchants_ids_with_pubkeys() -> List[str]: - rows = await db.fetchall( +async def get_merchants_ids_with_pubkeys() -> List[Tuple[str, str]]: + rows: list[dict] = await db.fetchall( """SELECT id, public_key FROM nostrmarket.merchants""", ) - return [(row[0], row[1]) for row in rows] + return [(row["id"], row["public_key"]) for row in rows] async def get_merchant_for_user(user_id: str) -> Optional[Merchant]: - row = await db.fetchone( - """SELECT * FROM nostrmarket.merchants WHERE user_id = ? """, - (user_id,), + row: dict = await db.fetchone( + """SELECT * FROM nostrmarket.merchants WHERE user_id = :user_id """, + {"user_id": user_id}, ) return Merchant.from_row(row) if row else None @@ -102,29 +106,31 @@ async def get_merchant_for_user(user_id: str) -> Optional[Merchant]: async def delete_merchant(merchant_id: str) -> None: await db.execute( - "DELETE FROM nostrmarket.merchants WHERE id = ?", - (merchant_id,), + "DELETE FROM nostrmarket.merchants WHERE id = :id", + { + "id": merchant_id, + }, ) ######################################## ZONES ######################################## -async def create_zone(merchant_id: str, data: PartialZone) -> Zone: +async def create_zone(merchant_id: str, data: Zone) -> Zone: zone_id = data.id or urlsafe_short_hash() await db.execute( - f""" + """ INSERT INTO nostrmarket.zones (id, merchant_id, name, currency, cost, regions) - VALUES (?, ?, ?, ?, ?, ?) + VALUES (:id, :merchant_id, :name, :currency, :cost, :regions) """, - ( - zone_id, - merchant_id, - data.name, - data.currency, - data.cost, - json.dumps(data.countries), - ), + { + "id": zone_id, + "merchant_id": merchant_id, + "name": data.name, + "currency": data.currency, + "cost": data.cost, + "regions": json.dumps(data.countries), + }, ) zone = await get_zone(merchant_id, zone_id) @@ -134,26 +140,38 @@ async def create_zone(merchant_id: str, data: PartialZone) -> Zone: async def update_zone(merchant_id: str, z: Zone) -> Optional[Zone]: await db.execute( - f"UPDATE nostrmarket.zones SET name = ?, cost = ?, regions = ? WHERE id = ? AND merchant_id = ?", - (z.name, z.cost, json.dumps(z.countries), z.id, merchant_id), - ) + """ + UPDATE nostrmarket.zones + SET name = :name, cost = :cost, regions = :regions + WHERE id = :id AND merchant_id = :merchant_id + """, + { + "name": z.name, + "cost": z.cost, + "regions": json.dumps(z.countries), + "id": z.id, + "merchant_id": merchant_id, + }, + ) + assert z.id return await get_zone(merchant_id, z.id) async def get_zone(merchant_id: str, zone_id: str) -> Optional[Zone]: - row = await db.fetchone( - "SELECT * FROM nostrmarket.zones WHERE merchant_id = ? AND id = ?", - ( - merchant_id, - zone_id, - ), + row: dict = await db.fetchone( + "SELECT * FROM nostrmarket.zones WHERE merchant_id = :merchant_id AND id = :id", + { + "merchant_id": merchant_id, + "id": zone_id, + }, ) return Zone.from_row(row) if row else None async def get_zones(merchant_id: str) -> List[Zone]: - rows = await db.fetchall( - "SELECT * FROM nostrmarket.zones WHERE merchant_id = ?", (merchant_id,) + rows: list[dict] = await db.fetchall( + "SELECT * FROM nostrmarket.zones WHERE merchant_id = :merchant_id", + {"merchant_id": merchant_id}, ) return [Zone.from_row(row) for row in rows] @@ -161,47 +179,55 @@ async def get_zones(merchant_id: str) -> List[Zone]: async def delete_zone(merchant_id: str, zone_id: str) -> None: await db.execute( - "DELETE FROM nostrmarket.zones WHERE merchant_id = ? AND id = ?", - ( - merchant_id, - zone_id, - ), + "DELETE FROM nostrmarket.zones WHERE merchant_id = :merchant_id AND id = :id", + { + "merchant_id": merchant_id, + "id": zone_id, + }, ) async def delete_merchant_zones(merchant_id: str) -> None: await db.execute( - "DELETE FROM nostrmarket.zones WHERE merchant_id = ?", (merchant_id,) + "DELETE FROM nostrmarket.zones WHERE merchant_id = ?", + {"merchant_id": merchant_id}, ) ######################################## STALL ######################################## -async def create_stall(merchant_id: str, data: PartialStall) -> Stall: +async def create_stall(merchant_id: str, data: Stall) -> Stall: stall_id = data.id or urlsafe_short_hash() await db.execute( - f""" + """ INSERT INTO nostrmarket.stalls - (merchant_id, id, wallet, name, currency, pending, event_id, event_created_at, zones, meta) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + ( + merchant_id, id, wallet, name, currency, + pending, event_id, event_created_at, zones, meta + ) + VALUES + ( + :merchant_id, :id, :wallet, :name, :currency, + :pending, :event_id, :event_created_at, :zones, :meta + ) ON CONFLICT(id) DO NOTHING """, - ( - merchant_id, - stall_id, - data.wallet, - data.name, - data.currency, - data.pending, - data.event_id, - data.event_created_at, - json.dumps( + { + "merchant_id": merchant_id, + "id": stall_id, + "wallet": data.wallet, + "name": data.name, + "currency": data.currency, + "pending": data.pending, + "event_id": data.event_id, + "event_created_at": data.event_created_at, + "zones": json.dumps( [z.dict() for z in data.shipping_zones] ), # todo: cost is float. should be int for sats - json.dumps(data.config.dict()), - ), + "meta": json.dumps(data.config.dict()), + }, ) stall = await get_stall(merchant_id, stall_id) @@ -210,107 +236,129 @@ async def create_stall(merchant_id: str, data: PartialStall) -> Stall: async def get_stall(merchant_id: str, stall_id: str) -> Optional[Stall]: - row = await db.fetchone( - "SELECT * FROM nostrmarket.stalls WHERE merchant_id = ? AND id = ?", - ( - merchant_id, - stall_id, - ), + row: dict = await db.fetchone( + """ + SELECT * FROM nostrmarket.stalls + WHERE merchant_id = :merchant_id AND id = :id + """, + { + "merchant_id": merchant_id, + "id": stall_id, + }, ) return Stall.from_row(row) if row else None async def get_stalls(merchant_id: str, pending: Optional[bool] = False) -> List[Stall]: - rows = await db.fetchall( - "SELECT * FROM nostrmarket.stalls WHERE merchant_id = ? AND pending = ?", - ( - merchant_id, - pending, - ), + rows: list[dict] = await db.fetchall( + """ + SELECT * FROM nostrmarket.stalls + WHERE merchant_id = :merchant_id AND pending = :pending + """, + { + "merchant_id": merchant_id, + "pending": pending, + }, ) return [Stall.from_row(row) for row in rows] async def get_last_stall_update_time() -> int: - row = await db.fetchone( + row: dict = await db.fetchone( """ - SELECT event_created_at FROM nostrmarket.stalls + SELECT event_created_at FROM nostrmarket.stalls ORDER BY event_created_at DESC LIMIT 1 """, - (), + {}, ) - return row[0] or 0 if row else 0 + return row["event_created_at"] or 0 if row else 0 async def update_stall(merchant_id: str, stall: Stall) -> Optional[Stall]: await db.execute( - f""" - UPDATE nostrmarket.stalls SET wallet = ?, name = ?, currency = ?, pending = ?, event_id = ?, event_created_at = ?, zones = ?, meta = ? - WHERE merchant_id = ? AND id = ? + """ + UPDATE nostrmarket.stalls + SET wallet = :wallet, name = :name, currency = :currency, + pending = :pending, event_id = :event_id, + event_created_at = :event_created_at, + zones = :zones, meta = :meta + WHERE merchant_id = :merchant_id AND id = :id """, - ( - stall.wallet, - stall.name, - stall.currency, - stall.pending, - stall.event_id, - stall.event_created_at, - json.dumps( + { + "wallet": stall.wallet, + "name": stall.name, + "currency": stall.currency, + "pending": stall.pending, + "event_id": stall.event_id, + "event_created_at": stall.event_created_at, + "zones": json.dumps( [z.dict() for z in stall.shipping_zones] ), # todo: cost is float. should be int for sats - json.dumps(stall.config.dict()), - merchant_id, - stall.id, - ), + "meta": json.dumps(stall.config.dict()), + "merchant_id": merchant_id, + "id": stall.id, + }, ) + assert stall.id return await get_stall(merchant_id, stall.id) async def delete_stall(merchant_id: str, stall_id: str) -> None: await db.execute( - "DELETE FROM nostrmarket.stalls WHERE merchant_id =? AND id = ?", - ( - merchant_id, - stall_id, - ), + """ + DELETE FROM nostrmarket.stalls + WHERE merchant_id = :merchant_id AND id = :id + """, + { + "merchant_id": merchant_id, + "id": stall_id, + }, ) async def delete_merchant_stalls(merchant_id: str) -> None: await db.execute( - "DELETE FROM nostrmarket.stalls WHERE merchant_id = ?", - (merchant_id,), + "DELETE FROM nostrmarket.stalls WHERE merchant_id = :merchant_id", + {"merchant_id": merchant_id}, ) -######################################## PRODUCTS ######################################## +######################################## PRODUCTS ###################################### -async def create_product(merchant_id: str, data: PartialProduct) -> Product: +async def create_product(merchant_id: str, data: Product) -> Product: product_id = data.id or urlsafe_short_hash() await db.execute( - f""" - INSERT INTO nostrmarket.products - (merchant_id, id, stall_id, name, price, quantity, active, pending, event_id, event_created_at, image_urls, category_list, meta) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + """ + INSERT INTO nostrmarket.products + ( + merchant_id, id, stall_id, name, price, quantity, + active, pending, event_id, event_created_at, + image_urls, category_list, meta + ) + VALUES ( + :merchant_id, :id, :stall_id, :name, :price, :quantity, + :active, :pending, :event_id, :event_created_at, + :image_urls, :category_list, :meta + ) ON CONFLICT(id) DO NOTHING """, - ( - merchant_id, - product_id, - data.stall_id, - data.name, - data.price, - data.quantity, - data.active, - data.pending, - data.event_id, - data.event_created_at, - json.dumps(data.images), - json.dumps(data.categories), - json.dumps(data.config.dict()), - ), + { + "merchant_id": merchant_id, + "id": product_id, + "stall_id": data.stall_id, + "name": data.name, + "price": data.price, + "quantity": data.quantity, + "active": data.active, + "pending": data.pending, + "event_id": data.event_id, + "event_created_at": data.event_created_at, + "image_urls": json.dumps(data.images), + "category_list": json.dumps(data.categories), + "meta": json.dumps(data.config.dict()), + }, ) product = await get_product(merchant_id, product_id) assert product, "Newly created product couldn't be retrieved" @@ -319,26 +367,30 @@ async def create_product(merchant_id: str, data: PartialProduct) -> Product: async def update_product(merchant_id: str, product: Product) -> Product: - + assert product.id await db.execute( - f""" - UPDATE nostrmarket.products set name = ?, price = ?, quantity = ?, active = ?, pending = ?, event_id =?, event_created_at = ?, image_urls = ?, category_list = ?, meta = ? - WHERE merchant_id = ? AND id = ? + """ + UPDATE nostrmarket.products + SET name = :name, price = :price, quantity = :quantity, + active = :active, pending = :pending, event_id =:event_id, + event_created_at = :event_created_at, image_urls = :image_urls, + category_list = :category_list, meta = :meta + WHERE merchant_id = :merchant_id AND id = :id """, - ( - product.name, - product.price, - product.quantity, - product.active, - product.pending, - product.event_id, - product.event_created_at, - json.dumps(product.images), - json.dumps(product.categories), - json.dumps(product.config.dict()), - merchant_id, - product.id, - ), + { + "name": product.name, + "price": product.price, + "quantity": product.quantity, + "active": product.active, + "pending": product.pending, + "event_id": product.event_id, + "event_created_at": product.event_created_at, + "image_urls": json.dumps(product.images), + "category_list": json.dumps(product.categories), + "meta": json.dumps(product.config.dict()), + "merchant_id": merchant_id, + "id": product.id, + }, ) updated_product = await get_product(merchant_id, product.id) assert updated_product, "Updated product couldn't be retrieved" @@ -350,33 +402,44 @@ async def update_product_quantity( product_id: str, new_quantity: int ) -> Optional[Product]: await db.execute( - f"UPDATE nostrmarket.products SET quantity = ? WHERE id = ?", - (new_quantity, product_id), + """ + UPDATE nostrmarket.products SET quantity = :quantity + WHERE id = :id + """, + {"quantity": new_quantity, "id": product_id}, ) - row = await db.fetchone( - "SELECT * FROM nostrmarket.products WHERE id = ?", - (product_id,), + row: dict = await db.fetchone( + "SELECT * FROM nostrmarket.products WHERE id = :id", + {"id": product_id}, ) return Product.from_row(row) if row else None async def get_product(merchant_id: str, product_id: str) -> Optional[Product]: - row = await db.fetchone( - "SELECT * FROM nostrmarket.products WHERE merchant_id =? AND id = ?", - ( - merchant_id, - product_id, - ), + row: dict = await db.fetchone( + """ + SELECT * FROM nostrmarket.products + WHERE merchant_id = :merchant_id AND id = :id + """, + { + "merchant_id": merchant_id, + "id": product_id, + }, ) + # TODO: remove from_row return Product.from_row(row) if row else None async def get_products( merchant_id: str, stall_id: str, pending: Optional[bool] = False ) -> List[Product]: - rows = await db.fetchall( - "SELECT * FROM nostrmarket.products WHERE merchant_id = ? AND stall_id = ? AND pending = ?", - (merchant_id, stall_id, pending), + rows: list[dict] = await db.fetchall( + """ + SELECT * FROM nostrmarket.products + WHERE merchant_id = :merchant_id + AND stall_id = :stall_id AND pending = :pending + """, + {"merchant_id": merchant_id, "stall_id": stall_id, "pending": pending}, ) return [Product.from_row(row) for row in rows] @@ -384,56 +447,67 @@ async def get_products( async def get_products_by_ids( merchant_id: str, product_ids: List[str] ) -> List[Product]: - q = ",".join(["?"] * len(product_ids)) - rows = await db.fetchall( + # todo: revisit + + keys = [] + values = {"merchant_id": merchant_id} + for i, v in enumerate(product_ids): + key = f"p_{i}" + values[key] = v + keys.append(f":{key}") + rows: list[dict] = await db.fetchall( f""" - SELECT id, stall_id, name, price, quantity, active, category_list, meta - FROM nostrmarket.products - WHERE merchant_id = ? AND pending = false AND id IN ({q}) + SELECT id, stall_id, name, price, quantity, active, category_list, meta + FROM nostrmarket.products + WHERE merchant_id = :merchant_id + AND pending = false AND id IN ({", ".join(keys)}) """, - (merchant_id, *product_ids), + values, ) return [Product.from_row(row) for row in rows] async def get_wallet_for_product(product_id: str) -> Optional[str]: - row = await db.fetchone( + row: dict = await db.fetchone( """ - SELECT s.wallet FROM nostrmarket.products p + SELECT s.wallet as wallet FROM nostrmarket.products p INNER JOIN nostrmarket.stalls s ON p.stall_id = s.id - WHERE p.id = ? AND p.pending = false AND s.pending = false + WHERE p.id = :product_id AND p.pending = false AND s.pending = false """, - (product_id,), + {"product_id": product_id}, ) - return row[0] if row else None + return row["wallet"] if row else None async def get_last_product_update_time() -> int: - row = await db.fetchone( + row: dict = await db.fetchone( """ - SELECT event_created_at FROM nostrmarket.products + SELECT event_created_at FROM nostrmarket.products ORDER BY event_created_at DESC LIMIT 1 """, - (), + {}, ) - return row[0] or 0 if row else 0 + return row["event_created_at"] or 0 if row else 0 async def delete_product(merchant_id: str, product_id: str) -> None: await db.execute( - "DELETE FROM nostrmarket.products WHERE merchant_id =? AND id = ?", - ( - merchant_id, - product_id, - ), + """ + DELETE FROM nostrmarket.products + WHERE merchant_id = :merchant_id AND id = :id + """, + { + "merchant_id": merchant_id, + "id": product_id, + }, ) async def delete_merchant_products(merchant_id: str) -> None: await db.execute( - "DELETE FROM nostrmarket.products WHERE merchant_id = ?", - (merchant_id,), + "DELETE FROM nostrmarket.products WHERE merchant_id = :merchant_id", + {"merchant_id": merchant_id}, ) @@ -442,42 +516,57 @@ async def delete_merchant_products(merchant_id: str) -> None: async def create_order(merchant_id: str, o: Order) -> Order: await db.execute( - f""" + """ INSERT INTO nostrmarket.orders ( - merchant_id, - id, - event_id, + merchant_id, + id, + event_id, event_created_at, merchant_public_key, - public_key, - address, - contact_data, - extra_data, + public_key, + address, + contact_data, + extra_data, order_items, shipping_id, - stall_id, - invoice_id, + stall_id, + invoice_id, total ) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + VALUES ( + :merchant_id, + :id, + :event_id, + :event_created_at, + :merchant_public_key, + :public_key, + :address, + :contact_data, + :extra_data, + :order_items, + :shipping_id, + :stall_id, + :invoice_id, + :total + ) ON CONFLICT(event_id) DO NOTHING """, - ( - merchant_id, - o.id, - o.event_id, - o.event_created_at, - o.merchant_public_key, - o.public_key, - o.address, - json.dumps(o.contact.dict() if o.contact else {}), - json.dumps(o.extra.dict()), - json.dumps([i.dict() for i in o.items]), - o.shipping_id, - o.stall_id, - o.invoice_id, - o.total, - ), + { + "merchant_id": merchant_id, + "id": o.id, + "event_id": o.event_id, + "event_created_at": o.event_created_at, + "merchant_public_key": o.merchant_public_key, + "public_key": o.public_key, + "address": o.address, + "contact_data": json.dumps(o.contact.dict() if o.contact else {}), + "extra_data": json.dumps(o.extra.dict()), + "order_items": json.dumps([i.dict() for i in o.items]), + "shipping_id": o.shipping_id, + "stall_id": o.stall_id, + "invoice_id": o.invoice_id, + "total": o.total, + }, ) order = await get_order(merchant_id, o.id) assert order, "Newly created order couldn't be retrieved" @@ -486,38 +575,54 @@ async def create_order(merchant_id: str, o: Order) -> Order: async def get_order(merchant_id: str, order_id: str) -> Optional[Order]: - row = await db.fetchone( - "SELECT * FROM nostrmarket.orders WHERE merchant_id =? AND id = ?", - ( - merchant_id, - order_id, - ), + row: dict = await db.fetchone( + """ + SELECT * FROM nostrmarket.orders + WHERE merchant_id = :merchant_id AND id = :id + """, + { + "merchant_id": merchant_id, + "id": order_id, + }, ) return Order.from_row(row) if row else None async def get_order_by_event_id(merchant_id: str, event_id: str) -> Optional[Order]: - row = await db.fetchone( - "SELECT * FROM nostrmarket.orders WHERE merchant_id =? AND event_id =?", - ( - merchant_id, - event_id, - ), + row: dict = await db.fetchone( + """ + SELECT * FROM nostrmarket.orders + WHERE merchant_id = :merchant_id AND event_id = :event_id + """, + { + "merchant_id": merchant_id, + "event_id": event_id, + }, ) return Order.from_row(row) if row else None async def get_orders(merchant_id: str, **kwargs) -> List[Order]: q = " AND ".join( - [f"{field[0]} = ?" for field in kwargs.items() if field[1] != None] - ) - values = () - if q: - q = f"AND {q}" - values = (v for v in kwargs.values() if v != None) - rows = await db.fetchall( - f"SELECT * FROM nostrmarket.orders WHERE merchant_id = ? {q} ORDER BY event_created_at DESC", - (merchant_id, *values), + [ + f"{field[0]} = :{field[0]}" + for field in kwargs.items() + if field[1] is not None + ] + ) + values = {"merchant_id": merchant_id} + for field in kwargs.items(): + if field[1] is None: + continue + values[field[0]] = field[1] + + rows: list[dict] = await db.fetchall( + f""" + SELECT * FROM nostrmarket.orders + WHERE merchant_id = :merchant_id {q} + ORDER BY event_created_at DESC + """, + values, ) return [Order.from_row(row) for row in rows] @@ -526,26 +631,48 @@ async def get_orders_for_stall( merchant_id: str, stall_id: str, **kwargs ) -> List[Order]: q = " AND ".join( - [f"{field[0]} = ?" for field in kwargs.items() if field[1] != None] - ) - values = () - if q: - q = f"AND {q}" - values = (v for v in kwargs.values() if v != None) - rows = await db.fetchall( - f"SELECT * FROM nostrmarket.orders WHERE merchant_id = ? AND stall_id = ? {q} ORDER BY time DESC", - (merchant_id, stall_id, *values), + [ + f"{field[0]} = :{field[0]}" + for field in kwargs.items() + if field[1] is not None + ] + ) + values = {"merchant_id": merchant_id, "stall_id": stall_id} + for field in kwargs.items(): + if field[1] is None: + continue + values[field[0]] = field[1] + + rows: list[dict] = await db.fetchall( + f""" + SELECT * FROM nostrmarket.orders + WHERE merchant_id = :merchant_id AND stall_id = :stall_id {q} + ORDER BY time DESC + """, + values, ) return [Order.from_row(row) for row in rows] async def update_order(merchant_id: str, order_id: str, **kwargs) -> Optional[Order]: - q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()]) + q = ", ".join( + [ + f"{field[0]} = :{field[0]}" + for field in kwargs.items() + if field[1] is not None + ] + ) + values = {"merchant_id": merchant_id, "id": order_id} + for field in kwargs.items(): + if field[1] is None: + continue + values[field[0]] = field[1] await db.execute( f""" - UPDATE nostrmarket.orders SET {q} WHERE merchant_id = ? and id = ? + UPDATE nostrmarket.orders + SET {q} WHERE merchant_id = :merchant_id and id = :id """, - (*kwargs.values(), merchant_id, order_id), + values, ) return await get_order(merchant_id, order_id) @@ -553,12 +680,12 @@ async def update_order(merchant_id: str, order_id: str, **kwargs) -> Optional[Or async def update_order_paid_status(order_id: str, paid: bool) -> Optional[Order]: await db.execute( - f"UPDATE nostrmarket.orders SET paid = ? WHERE id = ?", - (paid, order_id), + "UPDATE nostrmarket.orders SET paid = :paid WHERE id = :id", + {"paid": paid, "id": order_id}, ) - row = await db.fetchone( - "SELECT * FROM nostrmarket.orders WHERE id = ?", - (order_id,), + row: dict = await db.fetchone( + "SELECT * FROM nostrmarket.orders WHERE id = :id", + {"id": order_id}, ) return Order.from_row(row) if row else None @@ -567,25 +694,29 @@ async def update_order_shipped_status( merchant_id: str, order_id: str, shipped: bool ) -> Optional[Order]: await db.execute( - f"UPDATE nostrmarket.orders SET shipped = ? WHERE merchant_id = ? AND id = ?", - (shipped, merchant_id, order_id), + """ + UPDATE nostrmarket.orders + SET shipped = :shipped + WHERE merchant_id = :merchant_id AND id = :id + """, + {"shipped": shipped, "merchant_id": merchant_id, "id": order_id}, ) - row = await db.fetchone( - "SELECT * FROM nostrmarket.orders WHERE id = ?", - (order_id,), + row: dict = await db.fetchone( + "SELECT * FROM nostrmarket.orders WHERE id = :id", + {"id": order_id}, ) return Order.from_row(row) if row else None async def delete_merchant_orders(merchant_id: str) -> None: await db.execute( - "DELETE FROM nostrmarket.orders WHERE merchant_id = ?", - (merchant_id,), + "DELETE FROM nostrmarket.orders WHERE merchant_id = :merchant_id", + {"merchant_id": merchant_id}, ) -######################################## MESSAGES ########################################L +######################################## MESSAGES ###################################### async def create_direct_message( @@ -593,21 +724,29 @@ async def create_direct_message( ) -> DirectMessage: dm_id = urlsafe_short_hash() await db.execute( - f""" - INSERT INTO nostrmarket.direct_messages (merchant_id, id, event_id, event_created_at, message, public_key, type, incoming) - VALUES (?, ?, ?, ?, ?, ?, ?, ?) + """ + INSERT INTO nostrmarket.direct_messages + ( + merchant_id, id, event_id, event_created_at, + message, public_key, type, incoming + ) + VALUES + ( + :merchant_id, :id, :event_id, :event_created_at, + :message, :public_key, :type, :incoming + ) ON CONFLICT(event_id) DO NOTHING """, - ( - merchant_id, - dm_id, - dm.event_id, - dm.event_created_at, - dm.message, - dm.public_key, - dm.type, - dm.incoming, - ), + { + "merchant_id": merchant_id, + "id": dm_id, + "event_id": dm.event_id, + "event_created_at": dm.event_created_at, + "message": dm.message, + "public_key": dm.public_key, + "type": dm.type, + "incoming": dm.incoming, + }, ) if dm.event_id: msg = await get_direct_message_by_event_id(merchant_id, dm.event_id) @@ -618,12 +757,15 @@ async def create_direct_message( async def get_direct_message(merchant_id: str, dm_id: str) -> Optional[DirectMessage]: - row = await db.fetchone( - "SELECT * FROM nostrmarket.direct_messages WHERE merchant_id = ? AND id = ?", - ( - merchant_id, - dm_id, - ), + row: dict = await db.fetchone( + """ + SELECT * FROM nostrmarket.direct_messages + WHERE merchant_id = :merchant_id AND id = :id + """, + { + "merchant_id": merchant_id, + "id": dm_id, + }, ) return DirectMessage.from_row(row) if row else None @@ -631,75 +773,85 @@ async def get_direct_message(merchant_id: str, dm_id: str) -> Optional[DirectMes async def get_direct_message_by_event_id( merchant_id: str, event_id: str ) -> Optional[DirectMessage]: - row = await db.fetchone( - "SELECT * FROM nostrmarket.direct_messages WHERE merchant_id = ? AND event_id = ?", - ( - merchant_id, - event_id, - ), + row: dict = await db.fetchone( + """ + SELECT * FROM nostrmarket.direct_messages + WHERE merchant_id = :merchant_id AND event_id = :event_id + """, + { + "merchant_id": merchant_id, + "event_id": event_id, + }, ) return DirectMessage.from_row(row) if row else None async def get_direct_messages(merchant_id: str, public_key: str) -> List[DirectMessage]: - rows = await db.fetchall( - "SELECT * FROM nostrmarket.direct_messages WHERE merchant_id = ? AND public_key = ? ORDER BY event_created_at", - (merchant_id, public_key), + rows: list[dict] = await db.fetchall( + """ + SELECT * FROM nostrmarket.direct_messages + WHERE merchant_id = :merchant_id AND public_key = :public_key + ORDER BY event_created_at + """, + {"merchant_id": merchant_id, "public_key": public_key}, ) return [DirectMessage.from_row(row) for row in rows] async def get_orders_from_direct_messages(merchant_id: str) -> List[DirectMessage]: - rows = await db.fetchall( - "SELECT * FROM nostrmarket.direct_messages WHERE merchant_id = ? AND type >= 0 ORDER BY event_created_at, type", - (merchant_id), + rows: list[dict] = await db.fetchall( + """ + SELECT * FROM nostrmarket.direct_messages + WHERE merchant_id = :merchant_id AND type >= 0 ORDER BY event_created_at, type + """, + {"merchant_id": merchant_id}, ) return [DirectMessage.from_row(row) for row in rows] async def get_last_direct_messages_time(merchant_id: str) -> int: - row = await db.fetchone( + row: dict = await db.fetchone( """ - SELECT time FROM nostrmarket.direct_messages - WHERE merchant_id = ? ORDER BY time DESC LIMIT 1 + SELECT time FROM nostrmarket.direct_messages + WHERE merchant_id = :merchant_id ORDER BY time DESC LIMIT 1 """, - (merchant_id,), + {"merchant_id": merchant_id}, ) - return row[0] if row else 0 + return row["time"] if row else 0 async def get_last_direct_messages_created_at() -> int: - row = await db.fetchone( + row: dict = await db.fetchone( """ - SELECT event_created_at FROM nostrmarket.direct_messages + SELECT event_created_at FROM nostrmarket.direct_messages ORDER BY event_created_at DESC LIMIT 1 """, - (), + {}, ) - return row[0] if row else 0 + return row["event_created_at"] if row else 0 async def delete_merchant_direct_messages(merchant_id: str) -> None: await db.execute( - "DELETE FROM nostrmarket.direct_messages WHERE merchant_id = ?", - (merchant_id,), + "DELETE FROM nostrmarket.direct_messages WHERE merchant_id = :merchant_id", + {"merchant_id": merchant_id}, ) -######################################## CUSTOMERS ######################################## +######################################## CUSTOMERS ##################################### async def create_customer(merchant_id: str, data: Customer) -> Customer: await db.execute( - f""" + """ INSERT INTO nostrmarket.customers (merchant_id, public_key, meta) - VALUES (?, ?, ?) + VALUES (:merchant_id, :public_key, :meta) """, - ( - merchant_id, - data.public_key, - json.dumps(data.profile) if data.profile else "{}", - ), + { + "merchant_id": merchant_id, + "public_key": data.public_key, + "meta": json.dumps(data.profile) if data.profile else "{}", + }, ) customer = await get_customer(merchant_id, data.public_key) @@ -708,30 +860,34 @@ async def create_customer(merchant_id: str, data: Customer) -> Customer: async def get_customer(merchant_id: str, public_key: str) -> Optional[Customer]: - row = await db.fetchone( - "SELECT * FROM nostrmarket.customers WHERE merchant_id = ? AND public_key = ?", - ( - merchant_id, - public_key, - ), + row: dict = await db.fetchone( + """ + SELECT * FROM nostrmarket.customers + WHERE merchant_id = :merchant_id AND public_key = :public_key + """, + { + "merchant_id": merchant_id, + "public_key": public_key, + }, ) return Customer.from_row(row) if row else None async def get_customers(merchant_id: str) -> List[Customer]: - rows = await db.fetchall( - "SELECT * FROM nostrmarket.customers WHERE merchant_id = ?", (merchant_id,) + rows: list[dict] = await db.fetchall( + "SELECT * FROM nostrmarket.customers WHERE merchant_id = :merchant_id", + {"merchant_id": merchant_id}, ) return [Customer.from_row(row) for row in rows] async def get_all_unique_customers() -> List[Customer]: q = """ - SELECT public_key, MAX(merchant_id) as merchant_id, MAX(event_created_at) - FROM nostrmarket.customers + SELECT public_key, MAX(merchant_id) as merchant_id, MAX(event_created_at) + FROM nostrmarket.customers GROUP BY public_key """ - rows = await db.fetchall(q) + rows: list[dict] = await db.fetchall(q) return [Customer.from_row(row) for row in rows] @@ -739,27 +895,43 @@ async def update_customer_profile( public_key: str, event_created_at: int, profile: CustomerProfile ): await db.execute( - f"UPDATE nostrmarket.customers SET event_created_at = ?, meta = ? WHERE public_key = ?", - (event_created_at, json.dumps(profile.dict()), public_key), + """ + UPDATE nostrmarket.customers + SET event_created_at = :event_created_at, meta = :meta + WHERE public_key = :public_key + """, + { + "event_created_at": event_created_at, + "meta": json.dumps(profile.dict()), + "public_key": public_key, + }, ) async def increment_customer_unread_messages(merchant_id: str, public_key: str): await db.execute( - f"UPDATE nostrmarket.customers SET unread_messages = unread_messages + 1 WHERE merchant_id = ? AND public_key = ?", - ( - merchant_id, - public_key, - ), + """ + UPDATE nostrmarket.customers + SET unread_messages = unread_messages + 1 + WHERE merchant_id = :merchant_id AND public_key = :public_key + """, + { + "merchant_id": merchant_id, + "public_key": public_key, + }, ) # ??? two merchants async def update_customer_no_unread_messages(merchant_id: str, public_key: str): await db.execute( - f"UPDATE nostrmarket.customers SET unread_messages = 0 WHERE merchant_id =? AND public_key = ?", - ( - merchant_id, - public_key, - ), + """ + UPDATE nostrmarket.customers + SET unread_messages = 0 + WHERE merchant_id = :merchant_id AND public_key = :public_key + """, + { + "merchant_id": merchant_id, + "public_key": public_key, + }, ) diff --git a/description.md b/description.md index 9b3b5ba..6446ca7 100644 --- a/description.md +++ b/description.md @@ -5,8 +5,8 @@ Buy and sell things over Nostr, using NIP15 https://github.com/nostr-protocol/ni Nostr was partly based on the the previous version of this extension "Diagon Alley", so lends itself very well to buying and sellinng over Nostr. The Nostr Market extension includes: -* A merchant client to manage products, sales and communication with customers. -* A customer client to find and order products from merchants, communicate with merchants and track status of ordered products. -All communication happens over NIP04 encrypted DMs. +- A merchant client to manage products, sales and communication with customers. +- A customer client to find and order products from merchants, communicate with merchants and track status of ordered products. +All communication happens over NIP04 encrypted DMs. diff --git a/helpers.py b/helpers.py index f7e0d88..69d1f2f 100644 --- a/helpers.py +++ b/helpers.py @@ -1,7 +1,6 @@ import base64 -import json import secrets -from typing import Any, Optional, Tuple +from typing import Optional import secp256k1 from bech32 import bech32_decode, convertbits @@ -44,12 +43,14 @@ def encrypt_message(message: str, encryption_key, iv: Optional[bytes] = None) -> encryptor = cipher.encryptor() encrypted_message = encryptor.update(padded_data) + encryptor.finalize() - return f"{base64.b64encode(encrypted_message).decode()}?iv={base64.b64encode(iv).decode()}" + base64_message = base64.b64encode(encrypted_message).decode() + base64_iv = base64.b64encode(iv).decode() + return f"{base64_message}?iv={base64_iv}" -def sign_message_hash(private_key: str, hash: bytes) -> str: +def sign_message_hash(private_key: str, hash_: bytes) -> str: privkey = secp256k1.PrivateKey(bytes.fromhex(private_key)) - sig = privkey.schnorr_sign(hash, None, raw=True) + sig = privkey.schnorr_sign(hash_, None, raw=True) return sig.hex() diff --git a/migrations.py b/migrations.py index 005913e..c742565 100644 --- a/migrations.py +++ b/migrations.py @@ -1,5 +1,4 @@ async def m001_initial(db): - """ Initial merchants table. """ @@ -121,7 +120,10 @@ async def m001_initial(db): Create indexes for message fetching """ await db.execute( - "CREATE INDEX idx_messages_timestamp ON nostrmarket.direct_messages (time DESC)" + """ + CREATE INDEX idx_messages_timestamp + ON nostrmarket.direct_messages (time DESC) + """ ) await db.execute( "CREATE INDEX idx_event_id ON nostrmarket.direct_messages (event_id)" @@ -142,23 +144,26 @@ async def m001_initial(db): """ ) + async def m002_update_stall_and_product(db): await db.execute( - "ALTER TABLE nostrmarket.stalls ADD COLUMN pending BOOLEAN NOT NULL DEFAULT false;" - ) - await db.execute( - "ALTER TABLE nostrmarket.stalls ADD COLUMN event_id TEXT;" + """ + ALTER TABLE nostrmarket.stalls + ADD COLUMN pending BOOLEAN NOT NULL DEFAULT false; + """ ) + await db.execute("ALTER TABLE nostrmarket.stalls ADD COLUMN event_id TEXT;") await db.execute( "ALTER TABLE nostrmarket.stalls ADD COLUMN event_created_at INTEGER;" ) await db.execute( - "ALTER TABLE nostrmarket.products ADD COLUMN pending BOOLEAN NOT NULL DEFAULT false;" - ) - await db.execute( - "ALTER TABLE nostrmarket.products ADD COLUMN event_id TEXT;" + """ + ALTER TABLE nostrmarket.products + ADD COLUMN pending BOOLEAN NOT NULL DEFAULT false; + """ ) + await db.execute("ALTER TABLE nostrmarket.products ADD COLUMN event_id TEXT;") await db.execute( "ALTER TABLE nostrmarket.products ADD COLUMN event_created_at INTEGER;" ) @@ -166,15 +171,21 @@ async def m002_update_stall_and_product(db): async def m003_update_direct_message_type(db): await db.execute( - "ALTER TABLE nostrmarket.direct_messages ADD COLUMN type INTEGER NOT NULL DEFAULT -1;" + """ + ALTER TABLE nostrmarket.direct_messages + ADD COLUMN type INTEGER NOT NULL DEFAULT -1; + """ ) + async def m004_add_merchant_timestamp(db): - await db.execute( - f"ALTER TABLE nostrmarket.merchants ADD COLUMN time TIMESTAMP;" - ) + await db.execute("ALTER TABLE nostrmarket.merchants ADD COLUMN time TIMESTAMP;") + async def m005_update_product_activation(db): await db.execute( - "ALTER TABLE nostrmarket.products ADD COLUMN active BOOLEAN NOT NULL DEFAULT true;" - ) \ No newline at end of file + """ + ALTER TABLE nostrmarket.products + ADD COLUMN active BOOLEAN NOT NULL DEFAULT true; + """ + ) diff --git a/models.py b/models.py index be04ea5..1faab7c 100644 --- a/models.py +++ b/models.py @@ -2,12 +2,10 @@ import time from abc import abstractmethod from enum import Enum -from sqlite3 import Row from typing import Any, List, Optional, Tuple -from pydantic import BaseModel - from lnbits.utils.exchange_rates import btc_price, fiat_amount_as_satoshis +from pydantic import BaseModel from .helpers import ( decrypt_message, @@ -30,17 +28,17 @@ def to_nostr_delete_event(self, pubkey: str) -> NostrEvent: pass -######################################## MERCHANT ######################################## +######################################## MERCHANT ###################################### class MerchantProfile(BaseModel): - name: Optional[str] - about: Optional[str] - picture: Optional[str] + name: Optional[str] = None + about: Optional[str] = None + picture: Optional[str] = None class MerchantConfig(MerchantProfile): - event_id: Optional[str] + event_id: Optional[str] = None sync_from_nostr = False active: bool = False restore_in_progress: Optional[bool] = False @@ -56,8 +54,8 @@ class Merchant(PartialMerchant, Nostrable): id: str time: Optional[int] = 0 - def sign_hash(self, hash: bytes) -> str: - return sign_message_hash(self.private_key, hash) + def sign_hash(self, hash_: bytes) -> str: + return sign_message_hash(self.private_key, hash_) def decrypt_message(self, encrypted_message: str, public_key: str) -> str: encryption_key = get_shared_secret(self.private_key, public_key) @@ -82,8 +80,8 @@ def build_dm_event(self, message: str, to_pubkey: str) -> NostrEvent: return event @classmethod - def from_row(cls, row: Row) -> "Merchant": - merchant = cls(**dict(row)) + def from_row(cls, row: dict) -> "Merchant": + merchant = cls(**row) merchant.config = MerchantConfig(**json.loads(row["meta"])) return merchant @@ -123,20 +121,16 @@ def to_nostr_delete_event(self, pubkey: str) -> NostrEvent: ######################################## ZONES ######################################## -class PartialZone(BaseModel): - id: Optional[str] - name: Optional[str] +class Zone(BaseModel): + id: Optional[str] = None + name: Optional[str] = None currency: str cost: float countries: List[str] = [] - -class Zone(PartialZone): - id: str - @classmethod - def from_row(cls, row: Row) -> "Zone": - zone = cls(**dict(row)) + def from_row(cls, row: dict) -> "Zone": + zone = cls(**row) zone.countries = json.loads(row["regions"]) return zone @@ -145,12 +139,12 @@ def from_row(cls, row: Row) -> "Zone": class StallConfig(BaseModel): - image_url: Optional[str] - description: Optional[str] + image_url: Optional[str] = None + description: Optional[str] = None -class PartialStall(BaseModel): - id: Optional[str] +class Stall(BaseModel, Nostrable): + id: Optional[str] = None wallet: str name: str currency: str = "sat" @@ -159,8 +153,8 @@ class PartialStall(BaseModel): pending: bool = False """Last published nostr event for this Stall""" - event_id: Optional[str] - event_created_at: Optional[int] + event_id: Optional[str] = None + event_created_at: Optional[int] = None def validate_stall(self): for z in self.shipping_zones: @@ -169,10 +163,6 @@ def validate_stall(self): f"Sipping zone '{z.name}' has different currency than stall." ) - -class Stall(PartialStall, Nostrable): - id: str - def to_nostr_event(self, pubkey: str) -> NostrEvent: content = { "id": self.id, @@ -181,6 +171,7 @@ def to_nostr_event(self, pubkey: str) -> NostrEvent: "currency": self.currency, "shipping": [dict(z) for z in self.shipping_zones], } + assert self.id event = NostrEvent( pubkey=pubkey, created_at=round(time.time()), @@ -197,7 +188,7 @@ def to_nostr_delete_event(self, pubkey: str) -> NostrEvent: pubkey=pubkey, created_at=round(time.time()), kind=5, - tags=[["e", self.event_id]], + tags=[["e", self.event_id or ""]], content=f"Stall '{self.name}' deleted", ) delete_event.id = delete_event.event_id @@ -205,14 +196,14 @@ def to_nostr_delete_event(self, pubkey: str) -> NostrEvent: return delete_event @classmethod - def from_row(cls, row: Row) -> "Stall": - stall = cls(**dict(row)) + def from_row(cls, row: dict) -> "Stall": + stall = cls(**row) stall.config = StallConfig(**json.loads(row["meta"])) stall.shipping_zones = [Zone(**z) for z in json.loads(row["zones"])] return stall -######################################## PRODUCTS ######################################## +######################################## PRODUCTS ###################################### class ProductShippingCost(BaseModel): @@ -221,15 +212,15 @@ class ProductShippingCost(BaseModel): class ProductConfig(BaseModel): - description: Optional[str] - currency: Optional[str] + description: Optional[str] = None + currency: Optional[str] = None use_autoreply: Optional[bool] = False - autoreply_message: Optional[str] - shipping: Optional[List[ProductShippingCost]] = [] + autoreply_message: Optional[str] = None + shipping: List[ProductShippingCost] = [] -class PartialProduct(BaseModel): - id: Optional[str] +class Product(BaseModel, Nostrable): + id: Optional[str] = None stall_id: str name: str categories: List[str] = [] @@ -241,12 +232,8 @@ class PartialProduct(BaseModel): config: ProductConfig = ProductConfig() """Last published nostr event for this Product""" - event_id: Optional[str] - event_created_at: Optional[int] - - -class Product(PartialProduct, Nostrable): - id: str + event_id: Optional[str] = None + event_created_at: Optional[int] = None def to_nostr_event(self, pubkey: str) -> NostrEvent: content = { @@ -259,16 +246,17 @@ def to_nostr_event(self, pubkey: str) -> NostrEvent: "price": self.price, "quantity": self.quantity, "active": self.active, - "shipping": [dict(s) for s in self.config.shipping or []] + "shipping": [dict(s) for s in self.config.shipping or []], } categories = [["t", tag] for tag in self.categories] + assert self.id if self.active: event = NostrEvent( pubkey=pubkey, created_at=round(time.time()), kind=30018, - tags=[["d", self.id]] + categories, + tags=[["d", self.id], *categories], content=json.dumps(content, separators=(",", ":"), ensure_ascii=False), ) event.id = event.event_id @@ -282,7 +270,7 @@ def to_nostr_delete_event(self, pubkey: str) -> NostrEvent: pubkey=pubkey, created_at=round(time.time()), kind=5, - tags=[["e", self.event_id]], + tags=[["e", self.event_id or ""]], content=f"Product '{self.name}' deleted", ) delete_event.id = delete_event.event_id @@ -290,8 +278,8 @@ def to_nostr_delete_event(self, pubkey: str) -> NostrEvent: return delete_event @classmethod - def from_row(cls, row: Row) -> "Product": - product = cls(**dict(row)) + def from_row(cls, row: dict) -> "Product": + product = cls(**row) product.config = ProductConfig(**json.loads(row["meta"])) product.images = json.loads(row["image_urls"]) if "image_urls" in row else [] product.categories = json.loads(row["category_list"]) @@ -302,6 +290,12 @@ class ProductOverview(BaseModel): id: str name: str price: float + product_shipping_cost: Optional[float] = None + + @classmethod + def from_product(cls, p: Product) -> "ProductOverview": + assert p.id + return ProductOverview(id=p.id, name=p.name, price=p.price) ######################################## ORDERS ######################################## @@ -313,9 +307,9 @@ class OrderItem(BaseModel): class OrderContact(BaseModel): - nostr: Optional[str] - phone: Optional[str] - email: Optional[str] + nostr: Optional[str] = None + phone: Optional[str] = None + email: Optional[str] = None class OrderExtra(BaseModel): @@ -324,27 +318,33 @@ class OrderExtra(BaseModel): btc_price: str shipping_cost: float = 0 shipping_cost_sat: float = 0 - fail_message: Optional[str] + fail_message: Optional[str] = None @classmethod async def from_products(cls, products: List[Product]): currency = products[0].config.currency if len(products) else "sat" exchange_rate = ( - (await btc_price(currency)) if currency and currency != "sat" else 1 + await btc_price(currency) if currency and currency != "sat" else 1 + ) + + products_overview = [ProductOverview.from_product(p) for p in products] + return OrderExtra( + products=products_overview, + currency=currency or "sat", + btc_price=str(exchange_rate), ) - return OrderExtra(products=products, currency=currency, btc_price=exchange_rate) class PartialOrder(BaseModel): id: str - event_id: Optional[str] - event_created_at: Optional[int] + event_id: Optional[str] = None + event_created_at: Optional[int] = None public_key: str merchant_public_key: str shipping_id: str items: List[OrderItem] - contact: Optional[OrderContact] - address: Optional[str] + contact: Optional[OrderContact] = None + address: Optional[str] = None def validate_order(self): assert len(self.items) != 0, f"Order has no items. Order: '{self.id}'" @@ -383,10 +383,11 @@ async def costs_in_sats( } product_cost: float = 0 # todo + currency = "sat" for item in self.items: assert item.quantity > 0, "Quantity cannot be negative" - price = product_prices[item.product_id]["price"] - currency = product_prices[item.product_id]["currency"] + price = float(str(product_prices[item.product_id]["price"])) + currency = str(product_prices[item.product_id]["currency"]) if currency != "sat": price = await fiat_amount_as_satoshis(price, currency) product_cost += item.quantity * price @@ -404,30 +405,39 @@ def receipt( if len(products) == 0: return "[No Products]" receipt = "" - product_prices = {} + product_prices: dict[str, ProductOverview] = {} for p in products: product_shipping_cost = next( (s.cost for s in p.config.shipping if s.id == shipping_id), 0 ) - product_prices[p.id] = { - "name": p.name, - "price": p.price, - "product_shipping_cost": product_shipping_cost - } + assert p.id + product_prices[p.id] = ProductOverview( + id=p.id, + name=p.name, + price=p.price, + product_shipping_cost=product_shipping_cost, + ) currency = products[0].config.currency or "sat" products_cost: float = 0 # todo items_receipts = [] for item in self.items: prod = product_prices[item.product_id] - price = prod["price"] + prod["product_shipping_cost"] + price = prod.price + (prod.product_shipping_cost or 0) products_cost += item.quantity * price - items_receipts.append(f"""[{prod["name"]}: {item.quantity} x ({prod["price"]} + {prod["product_shipping_cost"]}) = {item.quantity * price} {currency}] """) + items_receipts.append( + f"""[{prod.name}: {item.quantity} x ({prod.price}""" + f""" + {prod.product_shipping_cost})""" + f""" = {item.quantity * price} {currency}] """ + ) receipt = "; ".join(items_receipts) - receipt += f"[Products cost: {products_cost} {currency}] [Stall shipping cost: {stall_shipping_cost} {currency}]; " + receipt += ( + f"[Products cost: {products_cost} {currency}] " + f"[Stall shipping cost: {stall_shipping_cost} {currency}]; " + ) receipt += f"[Total: {products_cost + stall_shipping_cost} {currency}]" return receipt @@ -439,23 +449,23 @@ class Order(PartialOrder): total: float paid: bool = False shipped: bool = False - time: Optional[int] + time: Optional[int] = None extra: OrderExtra @classmethod - def from_row(cls, row: Row) -> "Order": + def from_row(cls, row: dict) -> "Order": contact = OrderContact(**json.loads(row["contact_data"])) extra = OrderExtra(**json.loads(row["extra_data"])) items = [OrderItem(**z) for z in json.loads(row["order_items"])] - order = cls(**dict(row), contact=contact, items=items, extra=extra) + order = cls(**row, contact=contact, items=items, extra=extra) return order class OrderStatusUpdate(BaseModel): id: str - message: Optional[str] - paid: Optional[bool] - shipped: Optional[bool] + message: Optional[str] = None + paid: Optional[bool] = False + shipped: Optional[bool] = None class OrderReissue(BaseModel): @@ -470,11 +480,11 @@ class PaymentOption(BaseModel): class PaymentRequest(BaseModel): id: str - message: Optional[str] + message: Optional[str] = None payment_options: List[PaymentOption] -######################################## MESSAGE ######################################## +######################################## MESSAGE ####################################### class DirectMessageType(Enum): @@ -487,13 +497,13 @@ class DirectMessageType(Enum): class PartialDirectMessage(BaseModel): - event_id: Optional[str] - event_created_at: Optional[int] + event_id: Optional[str] = None + event_created_at: Optional[int] = None message: str public_key: str type: int = DirectMessageType.PLAIN_TEXT.value incoming: bool = False - time: Optional[int] + time: Optional[int] = None @classmethod def parse_message(cls, msg) -> Tuple[DirectMessageType, Optional[Any]]: @@ -511,29 +521,28 @@ class DirectMessage(PartialDirectMessage): id: str @classmethod - def from_row(cls, row: Row) -> "DirectMessage": - dm = cls(**dict(row)) - return dm + def from_row(cls, row: dict) -> "DirectMessage": + return cls(**row) -######################################## CUSTOMERS ######################################## +######################################## CUSTOMERS ##################################### class CustomerProfile(BaseModel): - name: Optional[str] - about: Optional[str] + name: Optional[str] = None + about: Optional[str] = None class Customer(BaseModel): merchant_id: str public_key: str - event_created_at: Optional[int] - profile: Optional[CustomerProfile] + event_created_at: Optional[int] = None + profile: Optional[CustomerProfile] = None unread_messages: int = 0 @classmethod - def from_row(cls, row: Row) -> "Customer": - customer = cls(**dict(row)) + def from_row(cls, row: dict) -> "Customer": + customer = cls(**row) customer.profile = ( CustomerProfile(**json.loads(row["meta"])) if "meta" in row else None ) diff --git a/nostr/event.py b/nostr/event.py index c92a4fa..a3216cf 100644 --- a/nostr/event.py +++ b/nostr/event.py @@ -13,7 +13,7 @@ class NostrEvent(BaseModel): kind: int tags: List[List[str]] = [] content: str = "" - sig: Optional[str] + sig: Optional[str] = None def serialize(self) -> List: return [0, self.pubkey, self.created_at, self.kind, self.tags, self.content] @@ -41,7 +41,7 @@ def check_signature(self): f"Invalid public key: '{self.pubkey}' for event '{self.id}'" ) - valid_signature = pub_key.schnorr_verify( + valid_signature = self.sig and pub_key.schnorr_verify( bytes.fromhex(event_id), bytes.fromhex(self.sig), None, raw=True ) if not valid_signature: diff --git a/nostr/nostr_client.py b/nostr/nostr_client.py index 7353e44..5e902e4 100644 --- a/nostr/nostr_client.py +++ b/nostr/nostr_client.py @@ -2,12 +2,12 @@ import json from asyncio import Queue from threading import Thread -from typing import Callable, List +from typing import Callable, List, Optional from loguru import logger from websocket import WebSocketApp -from lnbits.app import settings +from lnbits.settings import settings from lnbits.helpers import encrypt_internal_message, urlsafe_short_hash from .event import NostrEvent @@ -17,7 +17,7 @@ class NostrClient: def __init__(self): self.recieve_event_queue: Queue = Queue() self.send_req_queue: Queue = Queue() - self.ws: WebSocketApp = None + self.ws: Optional[WebSocketApp] = None self.subscription_id = "nostrmarket-" + urlsafe_short_hash()[:32] self.running = False @@ -30,7 +30,6 @@ def is_websocket_connected(self): async def connect_to_nostrclient_ws(self) -> WebSocketApp: logger.debug(f"Connecting to websockets for 'nostrclient' extension...") - relay_endpoint = encrypt_internal_message("relay") on_open, on_message, on_error, on_close = self._ws_handlers() ws = WebSocketApp( @@ -57,19 +56,18 @@ async def run_forever(self): await asyncio.sleep(5) req = await self.send_req_queue.get() + assert self.ws self.ws.send(json.dumps(req)) except Exception as ex: logger.warning(ex) await asyncio.sleep(60) - async def get_event(self): value = await self.recieve_event_queue.get() if isinstance(value, ValueError): raise value return value - async def publish_nostr_event(self, e: NostrEvent): await self.send_req_queue.put(["EVENT", e.dict()]) @@ -119,7 +117,7 @@ async def unsubscribe_with_delay(sub_id, d): asyncio.create_task(unsubscribe_with_delay(subscription_id, duration)) - async def user_profile_temp_subscribe(self, public_key: str, duration=5) -> List: + async def user_profile_temp_subscribe(self, public_key: str, duration=5): try: profile_filter = [{"kinds": [0], "authors": [public_key]}] subscription_id = "profile-" + urlsafe_short_hash()[:32] diff --git a/package.json b/package.json new file mode 100644 index 0000000..4917983 --- /dev/null +++ b/package.json @@ -0,0 +1,15 @@ +{ + "name": "nostrmarket", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "prettier": "^3.2.5", + "pyright": "^1.1.358" + } +} diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..09ccb84 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,2616 @@ +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. + +[[package]] +name = "aiosqlite" +version = "0.20.0" +description = "asyncio bridge to the standard sqlite3 module" +optional = false +python-versions = ">=3.8" +files = [ + {file = "aiosqlite-0.20.0-py3-none-any.whl", hash = "sha256:36a1deaca0cac40ebe32aac9977a6e2bbc7f5189f23f4a54d5908986729e5bd6"}, + {file = "aiosqlite-0.20.0.tar.gz", hash = "sha256:6d35c8c256637f4672f843c31021464090805bf925385ac39473fb16eaaca3d7"}, +] + +[package.dependencies] +typing_extensions = ">=4.0" + +[package.extras] +dev = ["attribution (==1.7.0)", "black (==24.2.0)", "coverage[toml] (==7.4.1)", "flake8 (==7.0.0)", "flake8-bugbear (==24.2.6)", "flit (==3.9.0)", "mypy (==1.8.0)", "ufmt (==2.3.0)", "usort (==1.0.8.post1)"] +docs = ["sphinx (==7.2.6)", "sphinx-mdinclude (==0.5.3)"] + +[[package]] +name = "anyio" +version = "4.6.2.post1" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +optional = false +python-versions = ">=3.9" +files = [ + {file = "anyio-4.6.2.post1-py3-none-any.whl", hash = "sha256:6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d"}, + {file = "anyio-4.6.2.post1.tar.gz", hash = "sha256:4c8bc31ccdb51c7f7bd251f51c609e038d63e34219b44aa86e47576389880b4c"}, +] + +[package.dependencies] +exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} +idna = ">=2.8" +sniffio = ">=1.1" +typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} + +[package.extras] +doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21.0b1)"] +trio = ["trio (>=0.26.1)"] + +[[package]] +name = "asn1crypto" +version = "1.5.1" +description = "Fast ASN.1 parser and serializer with definitions for private keys, public keys, certificates, CRL, OCSP, CMS, PKCS#3, PKCS#7, PKCS#8, PKCS#12, PKCS#5, X.509 and TSP" +optional = false +python-versions = "*" +files = [ + {file = "asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67"}, + {file = "asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c"}, +] + +[[package]] +name = "async-timeout" +version = "5.0.0" +description = "Timeout context manager for asyncio programs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "async_timeout-5.0.0-py3-none-any.whl", hash = "sha256:904719a4bd6e0520047d0ddae220aabee67b877f7ca17bf8cea20f67f6247ae0"}, + {file = "async_timeout-5.0.0.tar.gz", hash = "sha256:49675ec889daacfe65ff66d2dde7dd1447a6f4b2f23721022e4ba121f8772a85"}, +] + +[[package]] +name = "asyncpg" +version = "0.29.0" +description = "An asyncio PostgreSQL driver" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "asyncpg-0.29.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72fd0ef9f00aeed37179c62282a3d14262dbbafb74ec0ba16e1b1864d8a12169"}, + {file = "asyncpg-0.29.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:52e8f8f9ff6e21f9b39ca9f8e3e33a5fcdceaf5667a8c5c32bee158e313be385"}, + {file = "asyncpg-0.29.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9e6823a7012be8b68301342ba33b4740e5a166f6bbda0aee32bc01638491a22"}, + {file = "asyncpg-0.29.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:746e80d83ad5d5464cfbf94315eb6744222ab00aa4e522b704322fb182b83610"}, + {file = "asyncpg-0.29.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ff8e8109cd6a46ff852a5e6bab8b0a047d7ea42fcb7ca5ae6eaae97d8eacf397"}, + {file = "asyncpg-0.29.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:97eb024685b1d7e72b1972863de527c11ff87960837919dac6e34754768098eb"}, + {file = "asyncpg-0.29.0-cp310-cp310-win32.whl", hash = "sha256:5bbb7f2cafd8d1fa3e65431833de2642f4b2124be61a449fa064e1a08d27e449"}, + {file = "asyncpg-0.29.0-cp310-cp310-win_amd64.whl", hash = "sha256:76c3ac6530904838a4b650b2880f8e7af938ee049e769ec2fba7cd66469d7772"}, + {file = "asyncpg-0.29.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4900ee08e85af01adb207519bb4e14b1cae8fd21e0ccf80fac6aa60b6da37b4"}, + {file = "asyncpg-0.29.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a65c1dcd820d5aea7c7d82a3fdcb70e096f8f70d1a8bf93eb458e49bfad036ac"}, + {file = "asyncpg-0.29.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b52e46f165585fd6af4863f268566668407c76b2c72d366bb8b522fa66f1870"}, + {file = "asyncpg-0.29.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc600ee8ef3dd38b8d67421359779f8ccec30b463e7aec7ed481c8346decf99f"}, + {file = "asyncpg-0.29.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:039a261af4f38f949095e1e780bae84a25ffe3e370175193174eb08d3cecab23"}, + {file = "asyncpg-0.29.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6feaf2d8f9138d190e5ec4390c1715c3e87b37715cd69b2c3dfca616134efd2b"}, + {file = "asyncpg-0.29.0-cp311-cp311-win32.whl", hash = "sha256:1e186427c88225ef730555f5fdda6c1812daa884064bfe6bc462fd3a71c4b675"}, + {file = "asyncpg-0.29.0-cp311-cp311-win_amd64.whl", hash = "sha256:cfe73ffae35f518cfd6e4e5f5abb2618ceb5ef02a2365ce64f132601000587d3"}, + {file = "asyncpg-0.29.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6011b0dc29886ab424dc042bf9eeb507670a3b40aece3439944006aafe023178"}, + {file = "asyncpg-0.29.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b544ffc66b039d5ec5a7454667f855f7fec08e0dfaf5a5490dfafbb7abbd2cfb"}, + {file = "asyncpg-0.29.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d84156d5fb530b06c493f9e7635aa18f518fa1d1395ef240d211cb563c4e2364"}, + {file = "asyncpg-0.29.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54858bc25b49d1114178d65a88e48ad50cb2b6f3e475caa0f0c092d5f527c106"}, + {file = "asyncpg-0.29.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bde17a1861cf10d5afce80a36fca736a86769ab3579532c03e45f83ba8a09c59"}, + {file = "asyncpg-0.29.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:37a2ec1b9ff88d8773d3eb6d3784dc7e3fee7756a5317b67f923172a4748a175"}, + {file = "asyncpg-0.29.0-cp312-cp312-win32.whl", hash = "sha256:bb1292d9fad43112a85e98ecdc2e051602bce97c199920586be83254d9dafc02"}, + {file = "asyncpg-0.29.0-cp312-cp312-win_amd64.whl", hash = "sha256:2245be8ec5047a605e0b454c894e54bf2ec787ac04b1cb7e0d3c67aa1e32f0fe"}, + {file = "asyncpg-0.29.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0009a300cae37b8c525e5b449233d59cd9868fd35431abc470a3e364d2b85cb9"}, + {file = "asyncpg-0.29.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cad1324dbb33f3ca0cd2074d5114354ed3be2b94d48ddfd88af75ebda7c43cc"}, + {file = "asyncpg-0.29.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:012d01df61e009015944ac7543d6ee30c2dc1eb2f6b10b62a3f598beb6531548"}, + {file = "asyncpg-0.29.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:000c996c53c04770798053e1730d34e30cb645ad95a63265aec82da9093d88e7"}, + {file = "asyncpg-0.29.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e0bfe9c4d3429706cf70d3249089de14d6a01192d617e9093a8e941fea8ee775"}, + {file = "asyncpg-0.29.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:642a36eb41b6313ffa328e8a5c5c2b5bea6ee138546c9c3cf1bffaad8ee36dd9"}, + {file = "asyncpg-0.29.0-cp38-cp38-win32.whl", hash = "sha256:a921372bbd0aa3a5822dd0409da61b4cd50df89ae85150149f8c119f23e8c408"}, + {file = "asyncpg-0.29.0-cp38-cp38-win_amd64.whl", hash = "sha256:103aad2b92d1506700cbf51cd8bb5441e7e72e87a7b3a2ca4e32c840f051a6a3"}, + {file = "asyncpg-0.29.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5340dd515d7e52f4c11ada32171d87c05570479dc01dc66d03ee3e150fb695da"}, + {file = "asyncpg-0.29.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e17b52c6cf83e170d3d865571ba574577ab8e533e7361a2b8ce6157d02c665d3"}, + {file = "asyncpg-0.29.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f100d23f273555f4b19b74a96840aa27b85e99ba4b1f18d4ebff0734e78dc090"}, + {file = "asyncpg-0.29.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48e7c58b516057126b363cec8ca02b804644fd012ef8e6c7e23386b7d5e6ce83"}, + {file = "asyncpg-0.29.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f9ea3f24eb4c49a615573724d88a48bd1b7821c890c2effe04f05382ed9e8810"}, + {file = "asyncpg-0.29.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8d36c7f14a22ec9e928f15f92a48207546ffe68bc412f3be718eedccdf10dc5c"}, + {file = "asyncpg-0.29.0-cp39-cp39-win32.whl", hash = "sha256:797ab8123ebaed304a1fad4d7576d5376c3a006a4100380fb9d517f0b59c1ab2"}, + {file = "asyncpg-0.29.0-cp39-cp39-win_amd64.whl", hash = "sha256:cce08a178858b426ae1aa8409b5cc171def45d4293626e7aa6510696d46decd8"}, + {file = "asyncpg-0.29.0.tar.gz", hash = "sha256:d1c49e1f44fffafd9a55e1a9b101590859d881d639ea2922516f5d9c512d354e"}, +] + +[package.dependencies] +async-timeout = {version = ">=4.0.3", markers = "python_version < \"3.12.0\""} + +[package.extras] +docs = ["Sphinx (>=5.3.0,<5.4.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] +test = ["flake8 (>=6.1,<7.0)", "uvloop (>=0.15.3)"] + +[[package]] +name = "base58" +version = "2.1.1" +description = "Base58 and Base58Check implementation." +optional = false +python-versions = ">=3.5" +files = [ + {file = "base58-2.1.1-py3-none-any.whl", hash = "sha256:11a36f4d3ce51dfc1043f3218591ac4eb1ceb172919cebe05b52a5bcc8d245c2"}, + {file = "base58-2.1.1.tar.gz", hash = "sha256:c5d0cb3f5b6e81e8e35da5754388ddcc6d0d14b6c6a132cb93d69ed580a7278c"}, +] + +[package.extras] +tests = ["PyHamcrest (>=2.0.2)", "mypy", "pytest (>=4.6)", "pytest-benchmark", "pytest-cov", "pytest-flake8"] + +[[package]] +name = "bcrypt" +version = "4.2.0" +description = "Modern password hashing for your software and your servers" +optional = false +python-versions = ">=3.7" +files = [ + {file = "bcrypt-4.2.0-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:096a15d26ed6ce37a14c1ac1e48119660f21b24cba457f160a4b830f3fe6b5cb"}, + {file = "bcrypt-4.2.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c02d944ca89d9b1922ceb8a46460dd17df1ba37ab66feac4870f6862a1533c00"}, + {file = "bcrypt-4.2.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d84cf6d877918620b687b8fd1bf7781d11e8a0998f576c7aa939776b512b98d"}, + {file = "bcrypt-4.2.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:1bb429fedbe0249465cdd85a58e8376f31bb315e484f16e68ca4c786dcc04291"}, + {file = "bcrypt-4.2.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:655ea221910bcac76ea08aaa76df427ef8625f92e55a8ee44fbf7753dbabb328"}, + {file = "bcrypt-4.2.0-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:1ee38e858bf5d0287c39b7a1fc59eec64bbf880c7d504d3a06a96c16e14058e7"}, + {file = "bcrypt-4.2.0-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:0da52759f7f30e83f1e30a888d9163a81353ef224d82dc58eb5bb52efcabc399"}, + {file = "bcrypt-4.2.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3698393a1b1f1fd5714524193849d0c6d524d33523acca37cd28f02899285060"}, + {file = "bcrypt-4.2.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:762a2c5fb35f89606a9fde5e51392dad0cd1ab7ae64149a8b935fe8d79dd5ed7"}, + {file = "bcrypt-4.2.0-cp37-abi3-win32.whl", hash = "sha256:5a1e8aa9b28ae28020a3ac4b053117fb51c57a010b9f969603ed885f23841458"}, + {file = "bcrypt-4.2.0-cp37-abi3-win_amd64.whl", hash = "sha256:8f6ede91359e5df88d1f5c1ef47428a4420136f3ce97763e31b86dd8280fbdf5"}, + {file = "bcrypt-4.2.0-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:c52aac18ea1f4a4f65963ea4f9530c306b56ccd0c6f8c8da0c06976e34a6e841"}, + {file = "bcrypt-4.2.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3bbbfb2734f0e4f37c5136130405332640a1e46e6b23e000eeff2ba8d005da68"}, + {file = "bcrypt-4.2.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3413bd60460f76097ee2e0a493ccebe4a7601918219c02f503984f0a7ee0aebe"}, + {file = "bcrypt-4.2.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8d7bb9c42801035e61c109c345a28ed7e84426ae4865511eb82e913df18f58c2"}, + {file = "bcrypt-4.2.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3d3a6d28cb2305b43feac298774b997e372e56c7c7afd90a12b3dc49b189151c"}, + {file = "bcrypt-4.2.0-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:9c1c4ad86351339c5f320ca372dfba6cb6beb25e8efc659bedd918d921956bae"}, + {file = "bcrypt-4.2.0-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:27fe0f57bb5573104b5a6de5e4153c60814c711b29364c10a75a54bb6d7ff48d"}, + {file = "bcrypt-4.2.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:8ac68872c82f1add6a20bd489870c71b00ebacd2e9134a8aa3f98a0052ab4b0e"}, + {file = "bcrypt-4.2.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:cb2a8ec2bc07d3553ccebf0746bbf3d19426d1c6d1adbd4fa48925f66af7b9e8"}, + {file = "bcrypt-4.2.0-cp39-abi3-win32.whl", hash = "sha256:77800b7147c9dc905db1cba26abe31e504d8247ac73580b4aa179f98e6608f34"}, + {file = "bcrypt-4.2.0-cp39-abi3-win_amd64.whl", hash = "sha256:61ed14326ee023917ecd093ee6ef422a72f3aec6f07e21ea5f10622b735538a9"}, + {file = "bcrypt-4.2.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:39e1d30c7233cfc54f5c3f2c825156fe044efdd3e0b9d309512cc514a263ec2a"}, + {file = "bcrypt-4.2.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f4f4acf526fcd1c34e7ce851147deedd4e26e6402369304220250598b26448db"}, + {file = "bcrypt-4.2.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:1ff39b78a52cf03fdf902635e4c81e544714861ba3f0efc56558979dd4f09170"}, + {file = "bcrypt-4.2.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:373db9abe198e8e2c70d12b479464e0d5092cc122b20ec504097b5f2297ed184"}, + {file = "bcrypt-4.2.0.tar.gz", hash = "sha256:cf69eaf5185fd58f268f805b505ce31f9b9fc2d64b376642164e9244540c1221"}, +] + +[package.extras] +tests = ["pytest (>=3.2.1,!=3.3.0)"] +typecheck = ["mypy"] + +[[package]] +name = "bech32" +version = "1.2.0" +description = "Reference implementation for Bech32 and segwit addresses." +optional = false +python-versions = ">=3.5" +files = [ + {file = "bech32-1.2.0-py3-none-any.whl", hash = "sha256:990dc8e5a5e4feabbdf55207b5315fdd9b73db40be294a19b3752cde9e79d981"}, + {file = "bech32-1.2.0.tar.gz", hash = "sha256:7d6db8214603bd7871fcfa6c0826ef68b85b0abd90fa21c285a9c5e21d2bd899"}, +] + +[[package]] +name = "bitarray" +version = "2.9.3" +description = "efficient arrays of booleans -- C extension" +optional = false +python-versions = "*" +files = [ + {file = "bitarray-2.9.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2cf5f5400636c7dda797fd681795ce63932458620fe8c40955890380acba9f62"}, + {file = "bitarray-2.9.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3487b4718ffa5942fab777835ee36085f8dda7ec4bd0b28433efb117f84852b6"}, + {file = "bitarray-2.9.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:10f44b1e4994035408bea54d7bf0aec79744cad709706bedf28091a48bb7f1a4"}, + {file = "bitarray-2.9.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb5c16f97c65add6535748a9c98c70e7ca79759c38a2eb990127fef72f76111a"}, + {file = "bitarray-2.9.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:13dbfc42971ba84e9c4ba070f720df6570285a3f89187f07ef422efcb611c19f"}, + {file = "bitarray-2.9.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c28076acfbe7f9a5494d7ae98094a6e209c390c340938845f294818ebf5e4d3"}, + {file = "bitarray-2.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b7cdd21835936d9a66477836ca23b2cb63295142cb9d9158883e2c0f1f8f6bd"}, + {file = "bitarray-2.9.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9f60887ab3a46e507fa6f8544d8d4b0748da48718591dfe3fe80c62bdea60f10"}, + {file = "bitarray-2.9.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f75e1abd4a37cba3002521d3f5e2b50ef4f4a74342207cad3f52468411d5d8ba"}, + {file = "bitarray-2.9.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dc63da9695383c048b83f5ab77eab35a55bbb2e77c7b6e762eba219929b45b84"}, + {file = "bitarray-2.9.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:6fe5a57b859d9bc9c2fd27c78c4b7b83158faf984202de6fb44618caeebfff10"}, + {file = "bitarray-2.9.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1fe5a37bd9441a5ecc2f6e71b43df7176fa376a542ef97484310b8b46a45649a"}, + {file = "bitarray-2.9.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8a16e42c169ca818d6a15b5dd5acd5d2a26af0fa0588e1036e0e58d01f8387d4"}, + {file = "bitarray-2.9.3-cp310-cp310-win32.whl", hash = "sha256:5e6b5e7940af3474ffaa930cd1ce8215181cbe864d6b5ddb67a15d3c15e935cd"}, + {file = "bitarray-2.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:c63dbb99ef2ab1281871678624f9c9a5f1682b826e668ce559275ec488b3fa8b"}, + {file = "bitarray-2.9.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:49fb93b488d180f5c84b79fe687c585a84bf0295ff035d63e09ee24ce1da0558"}, + {file = "bitarray-2.9.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c2944fb83bbc2aa7f29a713bc4f8c1318e54fa0d06a72bedd350a3fb4a4b91d8"}, + {file = "bitarray-2.9.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3612d9d3788dc62f1922c917b1539f1cdf02cecc9faef8ae213a8b36093136ca"}, + {file = "bitarray-2.9.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90a9300cdb7c99b1e692bb790cba8acecee1a345a83e58e28c94a0d87c522237"}, + {file = "bitarray-2.9.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1211ed66acbbb221fd7554abf4206a384d79e6192d5cb95325c5c361bbb52a74"}, + {file = "bitarray-2.9.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:67757279386accf93eba76b8f97b5acf1664a3e350cbea5f300f53490f8764fd"}, + {file = "bitarray-2.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64e19c6a99c32f460c2613f797f77aa37d8e298891d00ea5355158cce80e11ec"}, + {file = "bitarray-2.9.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72734bd3775f43c5a75385730abb9f84fee6c627eb14f579de4be478f1615c8c"}, + {file = "bitarray-2.9.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a92703471b5d3316c7481bc1852f620f42f7a1b62be27f39d13694827635786f"}, + {file = "bitarray-2.9.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d5d77c81300ca430d4b195ccfbb629d6858258f541b6e96c6b11ec1563cd2681"}, + {file = "bitarray-2.9.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:3ba8a29c0d091c952ced1607ce715f5e0524899f24333a493807d00f5938463d"}, + {file = "bitarray-2.9.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:418171d035b191dbe5e86cd2bfb5c3e1ae7d947edc22857a897d1c7251674ae5"}, + {file = "bitarray-2.9.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1e0bd272eba256183be2a17488f9cb096d2e6d3435ecf2e28c1e0857c6d20749"}, + {file = "bitarray-2.9.3-cp311-cp311-win32.whl", hash = "sha256:cc3fd2b0637a619cf13e122bbcf4729ae214d5f25623675597e67c25f9edfe61"}, + {file = "bitarray-2.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:e1fc2a81a585dbe5e367682156e6350d908a56e2ffd6ca651b0af01994db596f"}, + {file = "bitarray-2.9.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:dc47be026f76f1728af00dc7140cec8483fe2f0c476bbf2a59ef47865e00ff96"}, + {file = "bitarray-2.9.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:82b091742ff511cdb06f90af0d2c22e7af3dbff9b8212e2e0d88dfef6a8570b3"}, + {file = "bitarray-2.9.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d5edb4302a0e3a3d1d0eeb891de3c615d4cb7a446fb41c21eecdcfb29400a6f"}, + {file = "bitarray-2.9.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb4786c5525069c19820549dd2f42d33632bc42959ad167138bd8ee5024b922b"}, + {file = "bitarray-2.9.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9bfe2de2b4df61ccb9244871a0fdf1fff83be0c1bd7187048c3cf7f81c5fe631"}, + {file = "bitarray-2.9.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:31e4f69538f95d2934587d957eea0d283162322dd1af29e57122b20b8cd60f92"}, + {file = "bitarray-2.9.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ca44908b2bc08d8995770018638d62626706864f9c599b7818225a12f3dbc2c"}, + {file = "bitarray-2.9.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:279f8de5d251ee521e365df29c927d9b5732f1ed4f373d2dbbd278fcbad94ff5"}, + {file = "bitarray-2.9.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c49bb631b38431c09ecd534d56ef04264397d24d18c4ee6653c84e14ae09d92d"}, + {file = "bitarray-2.9.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:192bffc93ee9a5b6c833c98d1dcc81f5633ddd726b85e18341387d0c1d51f691"}, + {file = "bitarray-2.9.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c516cec28c6511df51d87033f40ec420324a2247469b0c989d344f4d27ea37d2"}, + {file = "bitarray-2.9.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:66241cb9a1c1db294f46cd440141e57e8242874e38f3f61877f72d92ae14768a"}, + {file = "bitarray-2.9.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ab1f0e7631110c89bea7b605c0c35832333eb9cc97e5de05d71c76d42a1858c9"}, + {file = "bitarray-2.9.3-cp312-cp312-win32.whl", hash = "sha256:42aa5bee6fe8ad3385eaf5c6585016bbc38a7b75efb52ce5c6f8e00e05237dfa"}, + {file = "bitarray-2.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:dc3fd647d845b94fac3652390866f921f914a17f3807a031c826f68dae3f43e3"}, + {file = "bitarray-2.9.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:fcfcc1989e3e021a282624017b7fb754210f5332e933b1c3ebc79643727b6551"}, + {file = "bitarray-2.9.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:71b1e229a706798a9e106ca7b03d4c63455deb40b18c92950ec073a05a8f8285"}, + {file = "bitarray-2.9.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4bb49556d3d505d24c942a4206ad4d0d40e89fa3016a7ea6edc994d5c08d4a8e"}, + {file = "bitarray-2.9.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4466aa1e533a59d5f7fd37219d154ec3f2ba73fce3d8a2e11080ec475bc15fb"}, + {file = "bitarray-2.9.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a9b75adc0fd0bf278bea89dc3d679d74e10d2df98d3d074b7f3d36f323138818"}, + {file = "bitarray-2.9.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:701582bbbeac372b1cd8a3c9daf6c2336dc2d22e14373a6271d788bc4f2b6edc"}, + {file = "bitarray-2.9.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ea1f119668bbdbd68008031491515e84441e505163918819994b28f295f762c"}, + {file = "bitarray-2.9.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9f400bc18a70bfdb073532c3054ecd78a0e64f96ff7b6140adde5b122580ec2b"}, + {file = "bitarray-2.9.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:aacff5656fb3e15cede7d02903da2634d376aa928d7a81ec8df19b0724d7972a"}, + {file = "bitarray-2.9.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8a2ae42a14cbf766d4478d7101da6359b0648dd813e60eb3486ac56ad2f5add3"}, + {file = "bitarray-2.9.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:616698edb547d10f0b960cb9f2e8629c55a420dd4c2b1ab46706f49a1815621d"}, + {file = "bitarray-2.9.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f277c50ba184929dfeed39b6cf9468e3446093521b0aeb52bd54a21ca08f5473"}, + {file = "bitarray-2.9.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:661237739b385c90d8837d5e96b06de093cc6e610236977e198f88f5a979686e"}, + {file = "bitarray-2.9.3-cp313-cp313-win32.whl", hash = "sha256:68acec6c19d798051f178a1197b76f891985f683f95a4b12811b68e58b080f5a"}, + {file = "bitarray-2.9.3-cp313-cp313-win_amd64.whl", hash = "sha256:3055720afdcfd7e8f630fa16db7bed7e55c9d0a1f4756195e3b250e203f3b436"}, + {file = "bitarray-2.9.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:72bf17d0e7d8a4f645655a07999d23e42472cbf2100b8dad7ce26586075241d7"}, + {file = "bitarray-2.9.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cfd332b5f1ad8c4dc3cc79ecef33c19b42d8d8e6a39fd5c9ecb5855be0b9723"}, + {file = "bitarray-2.9.3-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d5b466ef1e48f25621c9d27e95deb5e33b8656827ed8aa530b972de73870bd1f"}, + {file = "bitarray-2.9.3-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:938cf26fdaf4d0adfac82d830c025523c5d36ddead0470b735286028231c1784"}, + {file = "bitarray-2.9.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0f766669e768ef9a2b23ecfa710b38b6a48da3f91755113c79320b207ae255d"}, + {file = "bitarray-2.9.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b6337c0c64044f35ddfb241143244aac707a68f34ae31a71dad115f773ccc8b"}, + {file = "bitarray-2.9.3-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:731b59540167f8b2b20f69f487ecee2339fc4657059906a16cb51acac17f89c3"}, + {file = "bitarray-2.9.3-cp36-cp36m-musllinux_1_2_i686.whl", hash = "sha256:4feed0539a9d6432361fc4d3820eea3a81fa631d542f166cf8430aad81a971da"}, + {file = "bitarray-2.9.3-cp36-cp36m-musllinux_1_2_ppc64le.whl", hash = "sha256:eb65c96a42e73f35175ec738d67992ffdf054c20abee3933cfcfa2343fa1187d"}, + {file = "bitarray-2.9.3-cp36-cp36m-musllinux_1_2_s390x.whl", hash = "sha256:4f40ceac94d182de6135759d81289683ff3e4cf0da709bc5826a7fe00d754114"}, + {file = "bitarray-2.9.3-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:5b29f7844080a281635a231a37e99f0bd6f567af6cf19f4f6d212137f99a9cdf"}, + {file = "bitarray-2.9.3-cp36-cp36m-win32.whl", hash = "sha256:947cf522a3b339b73114d12417fd848fa01303dbaa7883ced4c87688dba5637c"}, + {file = "bitarray-2.9.3-cp36-cp36m-win_amd64.whl", hash = "sha256:ea794ea60d514d68777a87a74106110db7a4bbc2c46720e67010e3071afefb95"}, + {file = "bitarray-2.9.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c7bc7cb79dcac8bdce23b305e671c06eaeffb012fa065b8c33bc51df7e1733f0"}, + {file = "bitarray-2.9.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d6380ad0f929ad9220abadd1c9b7234271c4b6ea9c753a88611d489e93a8f2e"}, + {file = "bitarray-2.9.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05f4e2451e2ad450b41ede8440e52c1fd798e81027e1dc2256292ec0787d3bf1"}, + {file = "bitarray-2.9.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7267885c98138f3707c710d5b08eedef150a3e5112c760cfe1200f3366fd7064"}, + {file = "bitarray-2.9.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:976957423cb41df8fe0eb811dbb53d8c5ab1ca3beec7a3ca7ff679be44a72714"}, + {file = "bitarray-2.9.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c0ec5141a69f73ed6ff17ea7344d5cc166e087095bfe3661dbb42b519e76aa16"}, + {file = "bitarray-2.9.3-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:218a1b7c0652a3c1020f903ded0f9768c3719fb6d43a6e9d346e985292992d35"}, + {file = "bitarray-2.9.3-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:cf0c9ebf2df280794244e1e12ed626357506ddaa2f0d6f69efe493ae7bbf4bf7"}, + {file = "bitarray-2.9.3-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:c450a04a7e091b57d4c0bd1531648522cd0ef26913ad0e5dea0432ea29b0e5c1"}, + {file = "bitarray-2.9.3-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:a212eb89a50e32ef4969387e44a7410447dc59587615e3966d090edc338a1b85"}, + {file = "bitarray-2.9.3-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:4269232026212ee6b73379b88a578107a6b36a6182307a49d5509686c7495261"}, + {file = "bitarray-2.9.3-cp37-cp37m-win32.whl", hash = "sha256:8a0fb358e6a43f216c3fb0871e2ac14c16563aec363c23bc2fbbb18f6201285d"}, + {file = "bitarray-2.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:a8368774cdc737eec8fce6f28d0abc095fbc0edccf8fab8d29fddc264b68def9"}, + {file = "bitarray-2.9.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:7d0724a4fef6ded914075a3385ea2d05afdeed567902f83490ed4e7e7e75d9bf"}, + {file = "bitarray-2.9.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0e11b37c6dff6f41ebc49914628824ceb8c8d6ebd0fda2ebe3c0fe0c63e8621e"}, + {file = "bitarray-2.9.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:085f4081d72c7468f82f722a9f113e03a1f7a4c132ef4c2a4e680c5d78b7db00"}, + {file = "bitarray-2.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b530b5fbed2900634fbc43f546e384abd72ad9c49795ff5bd6a93cac1aa9c4d8"}, + {file = "bitarray-2.9.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09ff88e4385967571146fb0d270442de39393d44198f4d108f3350cfd6486f0b"}, + {file = "bitarray-2.9.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a344bb212ddf87db4976a6711d274660a5d887da4fd3faafcdaa092152f85a6d"}, + {file = "bitarray-2.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc569c96b990f92fd5946d5b50501fee48b01a116a286d1de7961ebd9c6f06f3"}, + {file = "bitarray-2.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2fbbe7938ef8a7abe3e8519fa0578b51d2787f7171d3144e7d373551b5851fd"}, + {file = "bitarray-2.9.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:0b5912fab904507b47217509b01aa903d7f98b6e725e490a7f01661f4d9a4fa7"}, + {file = "bitarray-2.9.3-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:0c836ccfca9cf60927256738ef234dfe500565492eff269610cdd1bca56801d0"}, + {file = "bitarray-2.9.3-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:af0e4441ebf51c18fc450962f1e201c96f444d63b17cc8dcf7c0b05111bd4486"}, + {file = "bitarray-2.9.3-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:9e9b57175fb6fe76d7ddd0647e06a25f6e23f4b54b5febf337c5a840ab37dc3b"}, + {file = "bitarray-2.9.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:7f7de81721ae9492926bd067007ac974692182bb83fc8f0ba330a67f37a018bd"}, + {file = "bitarray-2.9.3-cp38-cp38-win32.whl", hash = "sha256:4beafb6b6e344385480df6611fdebfcb3579bbb40636ce1ddf5e72fb744e095f"}, + {file = "bitarray-2.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:d8eaeca98900bd6f06a29cdef57999813a67d314f661d14901d71e04f4cf9f00"}, + {file = "bitarray-2.9.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:413965d9d384aef90e58b959f4a39f1d5060b145c26080297b7b4cf23cf38faa"}, + {file = "bitarray-2.9.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2fbb56f2bb89c3a15304a6c0ea56013dc340a98337d9bbd7fc5c21451dc05f8c"}, + {file = "bitarray-2.9.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b8a84f39f7885627711473872d8fc58fc7a0a1e4ecd9ddf42daf9a3643432742"}, + {file = "bitarray-2.9.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45147a9c8580e857c1344d15bd49d2b4387777bd582a2ede11be2ba740653f28"}, + {file = "bitarray-2.9.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed255423dc60c6b2d5c0d90c13dea2962a31929767fdf1c525ab3210269e75c5"}, + {file = "bitarray-2.9.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4f5bd02671ea5c4ad52bbfe0e8e8197b6e8fa85dec1e93a4a05448c19354cc65"}, + {file = "bitarray-2.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1c99c58f044549c93fb6d4cda22678deccaed19845eaa2e6917b5b7ca058f2d"}, + {file = "bitarray-2.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:921ee87681e32e17d1849e11c96eb6a8a7edaa1269dd26831013daf8546bde05"}, + {file = "bitarray-2.9.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2ed97d8ec40c4658d9f9aa8f26cb473f44fa1dbccba3fa3fbe4a102e38c6a8d7"}, + {file = "bitarray-2.9.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:9d7f7db37edb9c50c9aad6a18f2e87dd7dc5ff2a33406821804a03263fedb2ca"}, + {file = "bitarray-2.9.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:292f726cdb9efc744ed0a1d7453c44151526648148a28d9a2495cc7c7b2c62a8"}, + {file = "bitarray-2.9.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2cc94784238782a9376f307b1aa9a85ce77b6eded9f82d2fe062db7fdb02c645"}, + {file = "bitarray-2.9.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5051436b1d318f6ce0df3b2f8a60bfa66a54c1d9e8719d6cb6b448140e7061f2"}, + {file = "bitarray-2.9.3-cp39-cp39-win32.whl", hash = "sha256:a3d436c686ce59fd0b93438ed2c0e1d3e1716e56bce64b874d05b9f49f1ca5d1"}, + {file = "bitarray-2.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:f168fc45664266a560f2cb28a327041b7f69d4a7faad8ab89e0a1dd7c270a70d"}, + {file = "bitarray-2.9.3-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ae36787299cff41f212aee33cfe1defee13979a41552665a412b6ca3fa8f7eb8"}, + {file = "bitarray-2.9.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42afe48abb8eeb386d93e7f1165ace1dd027f136a8a31edd2b20bc57a0c071d7"}, + {file = "bitarray-2.9.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:451ceecdb86bb95ae101b0d65c8c4524d692ae3666662fef8c89877ce17748c5"}, + {file = "bitarray-2.9.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4d67d3e3de2aede737b12cd75a84963700c941b77b579c14bd05517e05d7a9f"}, + {file = "bitarray-2.9.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2406d13ded84049b4238815a5821e44d6f58ba00fbb6b705b6ef8ccd88be8f03"}, + {file = "bitarray-2.9.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0db944fc2a048020fc940841ef46c0295b045d45a5a582cba69f78962a49a384"}, + {file = "bitarray-2.9.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25c603f141171a7d108773d5136d14e572c473e4cdb3fb464c39c8a138522eb2"}, + {file = "bitarray-2.9.3-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86c06b02705305cab0914d209caa24effda81316e2f2555a71a9aa399b75c5a5"}, + {file = "bitarray-2.9.3-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ddda45b24a802eaaca8f794e6267ff2b62de5fe7b900b76d6f662d95192bebf"}, + {file = "bitarray-2.9.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:81490623950d04870c6dd4d7e6df2eb68dd04eca8bec327895ebee8bbe0cc3c7"}, + {file = "bitarray-2.9.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a9e69ac6a514cc574891c24a50847022dac2fef8c3f4df530f92820a07337755"}, + {file = "bitarray-2.9.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:545c695ee69d26b41351ced4c76244d8b6225669fc0af3652ff8ed5a6b28325d"}, + {file = "bitarray-2.9.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fbb2e6daabd2a64d091ac7460b0c5c5f9268199ae9a8ce32737cf5273987f1fa"}, + {file = "bitarray-2.9.3-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a969e5cf63144b944ee8d0a0739f53ef1ae54725b5e01258d690a8995d880526"}, + {file = "bitarray-2.9.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:73bbb9301ac9000f869c51db2cc5fcc6541985d3fcdcfe6e02f90c9e672a00be"}, + {file = "bitarray-2.9.3-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7c07e346926488a85a48542d898f4168f3587ec42379fef0d18be301e08a3f27"}, + {file = "bitarray-2.9.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a26d8a14cd8ee496306f2afac34833502dd1ae826355af309333b6f252b23fe"}, + {file = "bitarray-2.9.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cef148ed37c892395ca182d6a235524165a9f765f4283d0a1ced891e7c43c67a"}, + {file = "bitarray-2.9.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94f35a8f0c8a50ee98a8bef9a070d0b68ecf623f20a2148cc039aba5557346a6"}, + {file = "bitarray-2.9.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b03207460daae828e2743874c84264e8d96a8c6156490279092b624cd5d2de08"}, + {file = "bitarray-2.9.3.tar.gz", hash = "sha256:9eff55cf189b0c37ba97156a00d640eb7392db58a8049be6f26ff2712b93fa89"}, +] + +[[package]] +name = "bitstring" +version = "4.2.3" +description = "Simple construction, analysis and modification of binary data." +optional = false +python-versions = ">=3.8" +files = [ + {file = "bitstring-4.2.3-py3-none-any.whl", hash = "sha256:20ed0036e2fcf0323acb0f92f0b7b178516a080f3e91061470aa019ac4ede404"}, + {file = "bitstring-4.2.3.tar.gz", hash = "sha256:e0c447af3fda0d114f77b88c2d199f02f97ee7e957e6d719f40f41cf15fbb897"}, +] + +[package.dependencies] +bitarray = ">=2.9.0,<3.0.0" + +[[package]] +name = "black" +version = "24.10.0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.9" +files = [ + {file = "black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812"}, + {file = "black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea"}, + {file = "black-24.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f"}, + {file = "black-24.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e"}, + {file = "black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad"}, + {file = "black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50"}, + {file = "black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392"}, + {file = "black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175"}, + {file = "black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3"}, + {file = "black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65"}, + {file = "black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f"}, + {file = "black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8"}, + {file = "black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981"}, + {file = "black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b"}, + {file = "black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2"}, + {file = "black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b"}, + {file = "black-24.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:17374989640fbca88b6a448129cd1745c5eb8d9547b464f281b251dd00155ccd"}, + {file = "black-24.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:63f626344343083322233f175aaf372d326de8436f5928c042639a4afbbf1d3f"}, + {file = "black-24.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfa1d0cb6200857f1923b602f978386a3a2758a65b52e0950299ea014be6800"}, + {file = "black-24.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cd9c95431d94adc56600710f8813ee27eea544dd118d45896bb734e9d7a0dc7"}, + {file = "black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d"}, + {file = "black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.10)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "bolt11" +version = "2.1.0" +description = "A library for encoding and decoding BOLT11 payment requests." +optional = false +python-versions = ">=3.8.1" +files = [ + {file = "bolt11-2.1.0-py3-none-any.whl", hash = "sha256:1535e441233414a9d8031a99fd3be07de4674bffda948033579404d44a42f614"}, + {file = "bolt11-2.1.0.tar.gz", hash = "sha256:177c63cd88d1eaba669eddb5937364676226253f2e9e5b77e8fe317ef32e62dd"}, +] + +[package.dependencies] +base58 = "*" +bech32 = "*" +bitstring = "*" +click = "*" +coincurve = "*" + +[[package]] +name = "certifi" +version = "2024.8.30" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, + {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, +] + +[[package]] +name = "cffi" +version = "1.17.1" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, + {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"}, + {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"}, + {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"}, + {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"}, + {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"}, + {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"}, + {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"}, + {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"}, + {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"}, + {file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"}, + {file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"}, + {file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"}, + {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"}, + {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, + {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, +] + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "cfgv" +version = "3.4.0" +description = "Validate configuration and produce human readable error messages." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, + {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.0" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-win32.whl", hash = "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-win32.whl", hash = "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-win32.whl", hash = "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-win32.whl", hash = "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-win32.whl", hash = "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca"}, + {file = "charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079"}, + {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "coincurve" +version = "20.0.0" +description = "Cross-platform Python CFFI bindings for libsecp256k1" +optional = false +python-versions = ">=3.8" +files = [ + {file = "coincurve-20.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d559b22828638390118cae9372a1bb6f6594f5584c311deb1de6a83163a0919b"}, + {file = "coincurve-20.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:33d7f6ebd90fcc550f819f7f2cce2af525c342aac07f0ccda46ad8956ad9d99b"}, + {file = "coincurve-20.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22d70dd55d13fd427418eb41c20fde0a20a5e5f016e2b1bb94710701e759e7e0"}, + {file = "coincurve-20.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46f18d481eaae72c169f334cde1fd22011a884e0c9c6adc3fdc1fd13df8236a3"}, + {file = "coincurve-20.0.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9de1ec57f43c3526bc462be58fb97910dc1fdd5acab6c71eda9f9719a5bd7489"}, + {file = "coincurve-20.0.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a6f007c44c726b5c0b3724093c0d4fb8e294f6b6869beb02d7473b21777473a3"}, + {file = "coincurve-20.0.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0ff1f3b81330db5092c24da2102e4fcba5094f14945b3eb40746456ceabdd6d9"}, + {file = "coincurve-20.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:82f7de97694d9343f26bd1c8e081b168e5f525894c12445548ce458af227f536"}, + {file = "coincurve-20.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:e905b4b084b4f3b61e5a5d58ac2632fd1d07b7b13b4c6d778335a6ca1dafd7a3"}, + {file = "coincurve-20.0.0-cp310-cp310-win_arm64.whl", hash = "sha256:3657bb5ed0baf1cf8cf356e7d44aa90a7902cc3dd4a435c6d4d0bed0553ad4f7"}, + {file = "coincurve-20.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:44087d1126d43925bf9a2391ce5601bf30ce0dba4466c239172dc43226696018"}, + {file = "coincurve-20.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5ccf0ba38b0f307a9b3ce28933f6c71dc12ef3a0985712ca09f48591afd597c8"}, + {file = "coincurve-20.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:566bc5986debdf8572b6be824fd4de03d533c49f3de778e29f69017ae3fe82d8"}, + {file = "coincurve-20.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4d70283168e146f025005c15406086513d5d35e89a60cf4326025930d45013a"}, + {file = "coincurve-20.0.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:763c6122dd7d5e7a81c86414ce360dbe9a2d4afa1ca6c853ee03d63820b3d0c5"}, + {file = "coincurve-20.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f00c361c356bcea386d47a191bb8ac60429f4b51c188966a201bfecaf306ff7f"}, + {file = "coincurve-20.0.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4af57bdadd2e64d117dd0b33cfefe76e90c7a6c496a7b034fc65fd01ec249b15"}, + {file = "coincurve-20.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a26437b7cbde13fb6e09261610b788ca2a0ca2195c62030afd1e1e0d1a62e035"}, + {file = "coincurve-20.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:ed51f8bba35e6c7676ad65539c3dbc35acf014fc402101fa24f6b0a15a74ab9e"}, + {file = "coincurve-20.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:594b840fc25d74118407edbbbc754b815f1bba9759dbf4f67f1c2b78396df2d3"}, + {file = "coincurve-20.0.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4df4416a6c0370d777aa725a25b14b04e45aa228da1251c258ff91444643f688"}, + {file = "coincurve-20.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1ccc3e4db55abf3fc0e604a187fdb05f0702bc5952e503d9a75f4ae6eeb4cb3a"}, + {file = "coincurve-20.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac8335b1658a2ef5b3eb66d52647742fe8c6f413ad5b9d5310d7ea6d8060d40f"}, + {file = "coincurve-20.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7ac025e485a0229fd5394e0bf6b4a75f8a4f6cee0dcf6f0b01a2ef05c5210ff"}, + {file = "coincurve-20.0.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e46e3f1c21b3330857bcb1a3a5b942f645c8bce912a8a2b252216f34acfe4195"}, + {file = "coincurve-20.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:df9ff9b17a1d27271bf476cf3fa92df4c151663b11a55d8cea838b8f88d83624"}, + {file = "coincurve-20.0.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4155759f071375699282e03b3d95fb473ee05c022641c077533e0d906311e57a"}, + {file = "coincurve-20.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0530b9dd02fc6f6c2916716974b79bdab874227f560c422801ade290e3fc5013"}, + {file = "coincurve-20.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:eacf9c0ce8739c84549a89c083b1f3526c8780b84517ee75d6b43d276e55f8a0"}, + {file = "coincurve-20.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:52a67bfddbd6224dfa42085c88ad176559801b57d6a8bd30d92ee040de88b7b3"}, + {file = "coincurve-20.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:61e951b1d695b62376f60519a84c4facaf756eeb9c5aff975bea0942833f185d"}, + {file = "coincurve-20.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4e9e548db77f4ea34c0d748dddefc698adb0ee3fab23ed19f80fb2118dac70f6"}, + {file = "coincurve-20.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8cdbf0da0e0809366fdfff236b7eb6e663669c7b1f46361a4c4d05f5b7e94c57"}, + {file = "coincurve-20.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d72222b4ecd3952e8ffcbf59bc7e0d1b181161ba170b60e5c8e1f359a43bbe7e"}, + {file = "coincurve-20.0.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9add43c4807f0c17a940ce4076334c28f51d09c145cd478400e89dcfb83fb59d"}, + {file = "coincurve-20.0.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bcc94cceea6ec8863815134083e6221a034b1ecef822d0277cf6ad2e70009b7f"}, + {file = "coincurve-20.0.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ffbdfef6a6d147988eabaed681287a9a7e6ba45ecc0a8b94ba62ad0a7656d97"}, + {file = "coincurve-20.0.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:13335c19c7e5f36eaba2a53c68073d981980d7dc7abfee68d29f2da887ccd24e"}, + {file = "coincurve-20.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:7fbfb8d16cf2bea2cf48fc5246d4cb0a06607d73bb5c57c007c9aed7509f855e"}, + {file = "coincurve-20.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4870047704cddaae7f0266a549c927407c2ba0ec92d689e3d2b511736812a905"}, + {file = "coincurve-20.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:81ce41263517b0a9f43cd570c87720b3c13324929584fa28d2e4095969b6015d"}, + {file = "coincurve-20.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:572083ccce6c7b514d482f25f394368f4ae888f478bd0b067519d33160ea2fcc"}, + {file = "coincurve-20.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee5bc78a31a2f1370baf28aaff3949bc48f940a12b0359d1cd2c4115742874e6"}, + {file = "coincurve-20.0.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2895d032e281c4e747947aae4bcfeef7c57eabfd9be22886c0ca4e1365c7c1f"}, + {file = "coincurve-20.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d3e2f21957ada0e1742edbde117bb41758fa8691b69c8d186c23e9e522ea71cd"}, + {file = "coincurve-20.0.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c2baa26b1aad1947ca07b3aa9e6a98940c5141c6bdd0f9b44d89e36da7282ffa"}, + {file = "coincurve-20.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7eacc7944ddf9e2b7448ecbe84753841ab9874b8c332a4f5cc3b2f184db9f4a2"}, + {file = "coincurve-20.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:c293c095dc690178b822cadaaeb81de3cc0d28f8bdf8216ed23551dcce153a26"}, + {file = "coincurve-20.0.0-cp39-cp39-win_arm64.whl", hash = "sha256:11a47083a0b7092d3eb50929f74ffd947c4a5e7035796b81310ea85289088c7a"}, + {file = "coincurve-20.0.0.tar.gz", hash = "sha256:872419e404300302e938849b6b92a196fabdad651060b559dc310e52f8392829"}, +] + +[package.dependencies] +asn1crypto = "*" +cffi = ">=1.3.0" + +[package.extras] +dev = ["coverage", "pytest", "pytest-benchmark"] + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "cryptography" +version = "42.0.8" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = false +python-versions = ">=3.7" +files = [ + {file = "cryptography-42.0.8-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:81d8a521705787afe7a18d5bfb47ea9d9cc068206270aad0b96a725022e18d2e"}, + {file = "cryptography-42.0.8-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:961e61cefdcb06e0c6d7e3a1b22ebe8b996eb2bf50614e89384be54c48c6b63d"}, + {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3ec3672626e1b9e55afd0df6d774ff0e953452886e06e0f1eb7eb0c832e8902"}, + {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e599b53fd95357d92304510fb7bda8523ed1f79ca98dce2f43c115950aa78801"}, + {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5226d5d21ab681f432a9c1cf8b658c0cb02533eece706b155e5fbd8a0cdd3949"}, + {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6b7c4f03ce01afd3b76cf69a5455caa9cfa3de8c8f493e0d3ab7d20611c8dae9"}, + {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:2346b911eb349ab547076f47f2e035fc8ff2c02380a7cbbf8d87114fa0f1c583"}, + {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ad803773e9df0b92e0a817d22fd8a3675493f690b96130a5e24f1b8fabbea9c7"}, + {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2f66d9cd9147ee495a8374a45ca445819f8929a3efcd2e3df6428e46c3cbb10b"}, + {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d45b940883a03e19e944456a558b67a41160e367a719833c53de6911cabba2b7"}, + {file = "cryptography-42.0.8-cp37-abi3-win32.whl", hash = "sha256:a0c5b2b0585b6af82d7e385f55a8bc568abff8923af147ee3c07bd8b42cda8b2"}, + {file = "cryptography-42.0.8-cp37-abi3-win_amd64.whl", hash = "sha256:57080dee41209e556a9a4ce60d229244f7a66ef52750f813bfbe18959770cfba"}, + {file = "cryptography-42.0.8-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:dea567d1b0e8bc5764b9443858b673b734100c2871dc93163f58c46a97a83d28"}, + {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4783183f7cb757b73b2ae9aed6599b96338eb957233c58ca8f49a49cc32fd5e"}, + {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0608251135d0e03111152e41f0cc2392d1e74e35703960d4190b2e0f4ca9c70"}, + {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dc0fdf6787f37b1c6b08e6dfc892d9d068b5bdb671198c72072828b80bd5fe4c"}, + {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:9c0c1716c8447ee7dbf08d6db2e5c41c688544c61074b54fc4564196f55c25a7"}, + {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fff12c88a672ab9c9c1cf7b0c80e3ad9e2ebd9d828d955c126be4fd3e5578c9e"}, + {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:cafb92b2bc622cd1aa6a1dce4b93307792633f4c5fe1f46c6b97cf67073ec961"}, + {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:31f721658a29331f895a5a54e7e82075554ccfb8b163a18719d342f5ffe5ecb1"}, + {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b297f90c5723d04bcc8265fc2a0f86d4ea2e0f7ab4b6994459548d3a6b992a14"}, + {file = "cryptography-42.0.8-cp39-abi3-win32.whl", hash = "sha256:2f88d197e66c65be5e42cd72e5c18afbfae3f741742070e3019ac8f4ac57262c"}, + {file = "cryptography-42.0.8-cp39-abi3-win_amd64.whl", hash = "sha256:fa76fbb7596cc5839320000cdd5d0955313696d9511debab7ee7278fc8b5c84a"}, + {file = "cryptography-42.0.8-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ba4f0a211697362e89ad822e667d8d340b4d8d55fae72cdd619389fb5912eefe"}, + {file = "cryptography-42.0.8-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:81884c4d096c272f00aeb1f11cf62ccd39763581645b0812e99a91505fa48e0c"}, + {file = "cryptography-42.0.8-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c9bb2ae11bfbab395bdd072985abde58ea9860ed84e59dbc0463a5d0159f5b71"}, + {file = "cryptography-42.0.8-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7016f837e15b0a1c119d27ecd89b3515f01f90a8615ed5e9427e30d9cdbfed3d"}, + {file = "cryptography-42.0.8-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5a94eccb2a81a309806027e1670a358b99b8fe8bfe9f8d329f27d72c094dde8c"}, + {file = "cryptography-42.0.8-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dec9b018df185f08483f294cae6ccac29e7a6e0678996587363dc352dc65c842"}, + {file = "cryptography-42.0.8-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:343728aac38decfdeecf55ecab3264b015be68fc2816ca800db649607aeee648"}, + {file = "cryptography-42.0.8-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:013629ae70b40af70c9a7a5db40abe5d9054e6f4380e50ce769947b73bf3caad"}, + {file = "cryptography-42.0.8.tar.gz", hash = "sha256:8d09d05439ce7baa8e9e95b07ec5b6c886f548deb7e0f69ef25f64b3bce842f2"}, +] + +[package.dependencies] +cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] +docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] +nox = ["nox"] +pep8test = ["check-sdist", "click", "mypy", "ruff"] +sdist = ["build"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test-randomorder = ["pytest-randomly"] + +[[package]] +name = "deprecated" +version = "1.2.14" +description = "Python @deprecated decorator to deprecate old python classes, functions or methods." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "Deprecated-1.2.14-py2.py3-none-any.whl", hash = "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c"}, + {file = "Deprecated-1.2.14.tar.gz", hash = "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3"}, +] + +[package.dependencies] +wrapt = ">=1.10,<2" + +[package.extras] +dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"] + +[[package]] +name = "distlib" +version = "0.3.9" +description = "Distribution utilities" +optional = false +python-versions = "*" +files = [ + {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, + {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, +] + +[[package]] +name = "dnspython" +version = "2.7.0" +description = "DNS toolkit" +optional = false +python-versions = ">=3.9" +files = [ + {file = "dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86"}, + {file = "dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1"}, +] + +[package.extras] +dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "hypercorn (>=0.16.0)", "mypy (>=1.8)", "pylint (>=3)", "pytest (>=7.4)", "pytest-cov (>=4.1.0)", "quart-trio (>=0.11.0)", "sphinx (>=7.2.0)", "sphinx-rtd-theme (>=2.0.0)", "twine (>=4.0.0)", "wheel (>=0.42.0)"] +dnssec = ["cryptography (>=43)"] +doh = ["h2 (>=4.1.0)", "httpcore (>=1.0.0)", "httpx (>=0.26.0)"] +doq = ["aioquic (>=1.0.0)"] +idna = ["idna (>=3.7)"] +trio = ["trio (>=0.23)"] +wmi = ["wmi (>=1.5.1)"] + +[[package]] +name = "ecdsa" +version = "0.19.0" +description = "ECDSA cryptographic signature library (pure python)" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.6" +files = [ + {file = "ecdsa-0.19.0-py2.py3-none-any.whl", hash = "sha256:2cea9b88407fdac7bbeca0833b189e4c9c53f2ef1e1eaa29f6224dbc809b707a"}, + {file = "ecdsa-0.19.0.tar.gz", hash = "sha256:60eaad1199659900dd0af521ed462b793bbdf867432b3948e87416ae4caf6bf8"}, +] + +[package.dependencies] +six = ">=1.9.0" + +[package.extras] +gmpy = ["gmpy"] +gmpy2 = ["gmpy2"] + +[[package]] +name = "email-validator" +version = "2.2.0" +description = "A robust email address syntax and deliverability validation library." +optional = false +python-versions = ">=3.8" +files = [ + {file = "email_validator-2.2.0-py3-none-any.whl", hash = "sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631"}, + {file = "email_validator-2.2.0.tar.gz", hash = "sha256:cb690f344c617a714f22e66ae771445a1ceb46821152df8e165c5f9a364582b7"}, +] + +[package.dependencies] +dnspython = ">=2.0.0" +idna = ">=2.0.0" + +[[package]] +name = "embit" +version = "0.8.0" +description = "A minimal bitcoin library for MicroPython and Python3 with a focus on embedded systems." +optional = false +python-versions = "*" +files = [ + {file = "embit-0.8.0.tar.gz", hash = "sha256:8bf4b10073c67400370ce523fb16f035fe759f6fdd987c579bdcc268d75ed770"}, +] + +[package.extras] +dev = ["black", "mkdocs", "mkdocs-material", "mkdocstrings[python]", "mypy", "pre-commit", "pytest", "pytest-cov", "requests"] + +[[package]] +name = "environs" +version = "9.5.0" +description = "simplified environment variable parsing" +optional = false +python-versions = ">=3.6" +files = [ + {file = "environs-9.5.0-py2.py3-none-any.whl", hash = "sha256:1e549569a3de49c05f856f40bce86979e7d5ffbbc4398e7f338574c220189124"}, + {file = "environs-9.5.0.tar.gz", hash = "sha256:a76307b36fbe856bdca7ee9161e6c466fd7fcffc297109a118c59b54e27e30c9"}, +] + +[package.dependencies] +marshmallow = ">=3.0.0" +python-dotenv = "*" + +[package.extras] +dev = ["dj-database-url", "dj-email-url", "django-cache-url", "flake8 (==4.0.1)", "flake8-bugbear (==21.9.2)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)", "pytest", "tox"] +django = ["dj-database-url", "dj-email-url", "django-cache-url"] +lint = ["flake8 (==4.0.1)", "flake8-bugbear (==21.9.2)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)"] +tests = ["dj-database-url", "dj-email-url", "django-cache-url", "pytest"] + +[[package]] +name = "exceptiongroup" +version = "1.2.2" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, + {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "fastapi" +version = "0.115.2" +description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fastapi-0.115.2-py3-none-any.whl", hash = "sha256:61704c71286579cc5a598763905928f24ee98bfcc07aabe84cfefb98812bbc86"}, + {file = "fastapi-0.115.2.tar.gz", hash = "sha256:3995739e0b09fa12f984bce8fa9ae197b35d433750d3d312422d846e283697ee"}, +] + +[package.dependencies] +pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" +starlette = ">=0.37.2,<0.41.0" +typing-extensions = ">=4.8.0" + +[package.extras] +all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] +standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=2.11.2)", "python-multipart (>=0.0.7)", "uvicorn[standard] (>=0.12.0)"] + +[[package]] +name = "fastapi-sso" +version = "0.15.0" +description = "FastAPI plugin to enable SSO to most common providers (such as Facebook login, Google login and login via Microsoft Office 365 Account)" +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "fastapi_sso-0.15.0-py3-none-any.whl", hash = "sha256:071e0c204181bdb6a8afad2714f9f6abad32bd8984b9e57500ff0d2b680fdb37"}, + {file = "fastapi_sso-0.15.0.tar.gz", hash = "sha256:9717c3e0f88ae7cccb43fa1827e6b85fa2621a4c41b21347ddebf719b40abc15"}, +] + +[package.dependencies] +fastapi = ">=0.80" +httpx = ">=0.23.0" +oauthlib = ">=3.1.0" +pydantic = {version = ">=1.8.0", extras = ["email"]} + +[[package]] +name = "filelock" +version = "3.16.1" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.8" +files = [ + {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, + {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, +] + +[package.extras] +docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] +typing = ["typing-extensions (>=4.12.2)"] + +[[package]] +name = "greenlet" +version = "3.1.1" +description = "Lightweight in-process concurrent programming" +optional = false +python-versions = ">=3.7" +files = [ + {file = "greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36b89d13c49216cadb828db8dfa6ce86bbbc476a82d3a6c397f0efae0525bdd0"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94b6150a85e1b33b40b1464a3f9988dcc5251d6ed06842abff82e42632fac120"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93147c513fac16385d1036b7e5b102c7fbbdb163d556b791f0f11eada7ba65dc"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da7a9bff22ce038e19bf62c4dd1ec8391062878710ded0a845bcf47cc0200617"}, + {file = "greenlet-3.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b2795058c23988728eec1f36a4e5e4ebad22f8320c85f3587b539b9ac84128d7"}, + {file = "greenlet-3.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ed10eac5830befbdd0c32f83e8aa6288361597550ba669b04c48f0f9a2c843c6"}, + {file = "greenlet-3.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:77c386de38a60d1dfb8e55b8c1101d68c79dfdd25c7095d51fec2dd800892b80"}, + {file = "greenlet-3.1.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d5e975ca70269d66d17dd995dafc06f1b06e8cb1ec1e9ed54c1d1e4a7c4cf26e"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2813dc3de8c1ee3f924e4d4227999285fd335d1bcc0d2be6dc3f1f6a318ec1"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e347b3bfcf985a05e8c0b7d462ba6f15b1ee1c909e2dcad795e49e91b152c383"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e8f8c9cb53cdac7ba9793c276acd90168f416b9ce36799b9b885790f8ad6c0a"}, + {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62ee94988d6b4722ce0028644418d93a52429e977d742ca2ccbe1c4f4a792511"}, + {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1776fd7f989fc6b8d8c8cb8da1f6b82c5814957264d1f6cf818d475ec2bf6395"}, + {file = "greenlet-3.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:48ca08c771c268a768087b408658e216133aecd835c0ded47ce955381105ba39"}, + {file = "greenlet-3.1.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9"}, + {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0"}, + {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942"}, + {file = "greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01"}, + {file = "greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e"}, + {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1"}, + {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c"}, + {file = "greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822"}, + {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01"}, + {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47da355d8687fd65240c364c90a31569a133b7b60de111c255ef5b606f2ae291"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98884ecf2ffb7d7fe6bd517e8eb99d31ff7855a840fa6d0d63cd07c037f6a981"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1d4aeb8891338e60d1ab6127af1fe45def5259def8094b9c7e34690c8858803"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db32b5348615a04b82240cc67983cb315309e88d444a288934ee6ceaebcad6cc"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dcc62f31eae24de7f8dce72134c8651c58000d3b1868e01392baea7c32c247de"}, + {file = "greenlet-3.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1d3755bcb2e02de341c55b4fca7a745a24a9e7212ac953f6b3a48d117d7257aa"}, + {file = "greenlet-3.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b8da394b34370874b4572676f36acabac172602abf054cbc4ac910219f3340af"}, + {file = "greenlet-3.1.1-cp37-cp37m-win32.whl", hash = "sha256:a0dfc6c143b519113354e780a50381508139b07d2177cb6ad6a08278ec655798"}, + {file = "greenlet-3.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:54558ea205654b50c438029505def3834e80f0869a70fb15b871c29b4575ddef"}, + {file = "greenlet-3.1.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:346bed03fe47414091be4ad44786d1bd8bef0c3fcad6ed3dee074a032ab408a9"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfc59d69fc48664bc693842bd57acfdd490acafda1ab52c7836e3fc75c90a111"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d21e10da6ec19b457b82636209cbe2331ff4306b54d06fa04b7c138ba18c8a81"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37b9de5a96111fc15418819ab4c4432e4f3c2ede61e660b1e33971eba26ef9ba"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ef9ea3f137e5711f0dbe5f9263e8c009b7069d8a1acea822bd5e9dae0ae49c8"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85f3ff71e2e60bd4b4932a043fbbe0f499e263c628390b285cb599154a3b03b1"}, + {file = "greenlet-3.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:95ffcf719966dd7c453f908e208e14cde192e09fde6c7186c8f1896ef778d8cd"}, + {file = "greenlet-3.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:03a088b9de532cbfe2ba2034b2b85e82df37874681e8c470d6fb2f8c04d7e4b7"}, + {file = "greenlet-3.1.1-cp38-cp38-win32.whl", hash = "sha256:8b8b36671f10ba80e159378df9c4f15c14098c4fd73a36b9ad715f057272fbef"}, + {file = "greenlet-3.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:7017b2be767b9d43cc31416aba48aab0d2309ee31b4dbf10a1d38fb7972bdf9d"}, + {file = "greenlet-3.1.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:396979749bd95f018296af156201d6211240e7a23090f50a8d5d18c370084dc3"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca9d0ff5ad43e785350894d97e13633a66e2b50000e8a183a50a88d834752d42"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f6ff3b14f2df4c41660a7dec01045a045653998784bf8cfcb5a525bdffffbc8f"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94ebba31df2aa506d7b14866fed00ac141a867e63143fe5bca82a8e503b36437"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73aaad12ac0ff500f62cebed98d8789198ea0e6f233421059fa68a5aa7220145"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63e4844797b975b9af3a3fb8f7866ff08775f5426925e1e0bbcfe7932059a12c"}, + {file = "greenlet-3.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7939aa3ca7d2a1593596e7ac6d59391ff30281ef280d8632fa03d81f7c5f955e"}, + {file = "greenlet-3.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d0028e725ee18175c6e422797c407874da24381ce0690d6b9396c204c7f7276e"}, + {file = "greenlet-3.1.1-cp39-cp39-win32.whl", hash = "sha256:5e06afd14cbaf9e00899fae69b24a32f2196c19de08fcb9f4779dd4f004e5e7c"}, + {file = "greenlet-3.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:3319aa75e0e0639bc15ff54ca327e8dc7a6fe404003496e3c6925cd3142e0e22"}, + {file = "greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467"}, +] + +[package.extras] +docs = ["Sphinx", "furo"] +test = ["objgraph", "psutil"] + +[[package]] +name = "grpcio" +version = "1.66.1" +description = "HTTP/2-based RPC framework" +optional = false +python-versions = ">=3.8" +files = [ + {file = "grpcio-1.66.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:4877ba180591acdf127afe21ec1c7ff8a5ecf0fe2600f0d3c50e8c4a1cbc6492"}, + {file = "grpcio-1.66.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:3750c5a00bd644c75f4507f77a804d0189d97a107eb1481945a0cf3af3e7a5ac"}, + {file = "grpcio-1.66.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:a013c5fbb12bfb5f927444b477a26f1080755a931d5d362e6a9a720ca7dbae60"}, + {file = "grpcio-1.66.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1b24c23d51a1e8790b25514157d43f0a4dce1ac12b3f0b8e9f66a5e2c4c132f"}, + {file = "grpcio-1.66.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7ffb8ea674d68de4cac6f57d2498fef477cef582f1fa849e9f844863af50083"}, + {file = "grpcio-1.66.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:307b1d538140f19ccbd3aed7a93d8f71103c5d525f3c96f8616111614b14bf2a"}, + {file = "grpcio-1.66.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1c17ebcec157cfb8dd445890a03e20caf6209a5bd4ac5b040ae9dbc59eef091d"}, + {file = "grpcio-1.66.1-cp310-cp310-win32.whl", hash = "sha256:ef82d361ed5849d34cf09105d00b94b6728d289d6b9235513cb2fcc79f7c432c"}, + {file = "grpcio-1.66.1-cp310-cp310-win_amd64.whl", hash = "sha256:292a846b92cdcd40ecca46e694997dd6b9be6c4c01a94a0dfb3fcb75d20da858"}, + {file = "grpcio-1.66.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:c30aeceeaff11cd5ddbc348f37c58bcb96da8d5aa93fed78ab329de5f37a0d7a"}, + {file = "grpcio-1.66.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8a1e224ce6f740dbb6b24c58f885422deebd7eb724aff0671a847f8951857c26"}, + {file = "grpcio-1.66.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:a66fe4dc35d2330c185cfbb42959f57ad36f257e0cc4557d11d9f0a3f14311df"}, + {file = "grpcio-1.66.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e3ba04659e4fce609de2658fe4dbf7d6ed21987a94460f5f92df7579fd5d0e22"}, + {file = "grpcio-1.66.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4573608e23f7e091acfbe3e84ac2045680b69751d8d67685ffa193a4429fedb1"}, + {file = "grpcio-1.66.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7e06aa1f764ec8265b19d8f00140b8c4b6ca179a6dc67aa9413867c47e1fb04e"}, + {file = "grpcio-1.66.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3885f037eb11f1cacc41f207b705f38a44b69478086f40608959bf5ad85826dd"}, + {file = "grpcio-1.66.1-cp311-cp311-win32.whl", hash = "sha256:97ae7edd3f3f91480e48ede5d3e7d431ad6005bfdbd65c1b56913799ec79e791"}, + {file = "grpcio-1.66.1-cp311-cp311-win_amd64.whl", hash = "sha256:cfd349de4158d797db2bd82d2020554a121674e98fbe6b15328456b3bf2495bb"}, + {file = "grpcio-1.66.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:a92c4f58c01c77205df6ff999faa008540475c39b835277fb8883b11cada127a"}, + {file = "grpcio-1.66.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:fdb14bad0835914f325349ed34a51940bc2ad965142eb3090081593c6e347be9"}, + {file = "grpcio-1.66.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:f03a5884c56256e08fd9e262e11b5cfacf1af96e2ce78dc095d2c41ccae2c80d"}, + {file = "grpcio-1.66.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ca2559692d8e7e245d456877a85ee41525f3ed425aa97eb7a70fc9a79df91a0"}, + {file = "grpcio-1.66.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84ca1be089fb4446490dd1135828bd42a7c7f8421e74fa581611f7afdf7ab761"}, + {file = "grpcio-1.66.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:d639c939ad7c440c7b2819a28d559179a4508783f7e5b991166f8d7a34b52815"}, + {file = "grpcio-1.66.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b9feb4e5ec8dc2d15709f4d5fc367794d69277f5d680baf1910fc9915c633524"}, + {file = "grpcio-1.66.1-cp312-cp312-win32.whl", hash = "sha256:7101db1bd4cd9b880294dec41a93fcdce465bdbb602cd8dc5bd2d6362b618759"}, + {file = "grpcio-1.66.1-cp312-cp312-win_amd64.whl", hash = "sha256:b0aa03d240b5539648d996cc60438f128c7f46050989e35b25f5c18286c86734"}, + {file = "grpcio-1.66.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:ecfe735e7a59e5a98208447293ff8580e9db1e890e232b8b292dc8bd15afc0d2"}, + {file = "grpcio-1.66.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:4825a3aa5648010842e1c9d35a082187746aa0cdbf1b7a2a930595a94fb10fce"}, + {file = "grpcio-1.66.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:f517fd7259fe823ef3bd21e508b653d5492e706e9f0ef82c16ce3347a8a5620c"}, + {file = "grpcio-1.66.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1fe60d0772831d96d263b53d83fb9a3d050a94b0e94b6d004a5ad111faa5b5b"}, + {file = "grpcio-1.66.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31a049daa428f928f21090403e5d18ea02670e3d5d172581670be006100db9ef"}, + {file = "grpcio-1.66.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6f914386e52cbdeb5d2a7ce3bf1fdfacbe9d818dd81b6099a05b741aaf3848bb"}, + {file = "grpcio-1.66.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bff2096bdba686019fb32d2dde45b95981f0d1490e054400f70fc9a8af34b49d"}, + {file = "grpcio-1.66.1-cp38-cp38-win32.whl", hash = "sha256:aa8ba945c96e73de29d25331b26f3e416e0c0f621e984a3ebdb2d0d0b596a3b3"}, + {file = "grpcio-1.66.1-cp38-cp38-win_amd64.whl", hash = "sha256:161d5c535c2bdf61b95080e7f0f017a1dfcb812bf54093e71e5562b16225b4ce"}, + {file = "grpcio-1.66.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:d0cd7050397b3609ea51727b1811e663ffda8bda39c6a5bb69525ef12414b503"}, + {file = "grpcio-1.66.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0e6c9b42ded5d02b6b1fea3a25f036a2236eeb75d0579bfd43c0018c88bf0a3e"}, + {file = "grpcio-1.66.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:c9f80f9fad93a8cf71c7f161778ba47fd730d13a343a46258065c4deb4b550c0"}, + {file = "grpcio-1.66.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5dd67ed9da78e5121efc5c510f0122a972216808d6de70953a740560c572eb44"}, + {file = "grpcio-1.66.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48b0d92d45ce3be2084b92fb5bae2f64c208fea8ceed7fccf6a7b524d3c4942e"}, + {file = "grpcio-1.66.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:4d813316d1a752be6f5c4360c49f55b06d4fe212d7df03253dfdae90c8a402bb"}, + {file = "grpcio-1.66.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9c9bebc6627873ec27a70fc800f6083a13c70b23a5564788754b9ee52c5aef6c"}, + {file = "grpcio-1.66.1-cp39-cp39-win32.whl", hash = "sha256:30a1c2cf9390c894c90bbc70147f2372130ad189cffef161f0432d0157973f45"}, + {file = "grpcio-1.66.1-cp39-cp39-win_amd64.whl", hash = "sha256:17663598aadbedc3cacd7bbde432f541c8e07d2496564e22b214b22c7523dac8"}, + {file = "grpcio-1.66.1.tar.gz", hash = "sha256:35334f9c9745add3e357e3372756fd32d925bd52c41da97f4dfdafbde0bf0ee2"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.66.1)"] + +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.7" +files = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] + +[[package]] +name = "http-ece" +version = "1.2.1" +description = "Encrypted Content Encoding for HTTP" +optional = false +python-versions = "*" +files = [ + {file = "http_ece-1.2.1.tar.gz", hash = "sha256:8c6ab23116bbf6affda894acfd5f2ca0fb8facbcbb72121c11c75c33e7ce8cff"}, +] + +[package.dependencies] +cryptography = ">=2.5" + +[[package]] +name = "httpcore" +version = "1.0.6" +description = "A minimal low-level HTTP client." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpcore-1.0.6-py3-none-any.whl", hash = "sha256:27b59625743b85577a8c0e10e55b50b5368a4f2cfe8cc7bcfa9cf00829c2682f"}, + {file = "httpcore-1.0.6.tar.gz", hash = "sha256:73f6dbd6eb8c21bbf7ef8efad555481853f5f6acdeaff1edb0694289269ee17f"}, +] + +[package.dependencies] +certifi = "*" +h11 = ">=0.13,<0.15" + +[package.extras] +asyncio = ["anyio (>=4.0,<5.0)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +trio = ["trio (>=0.22.0,<1.0)"] + +[[package]] +name = "httpx" +version = "0.27.0" +description = "The next generation HTTP client." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"}, + {file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"}, +] + +[package.dependencies] +anyio = "*" +certifi = "*" +httpcore = "==1.*" +idna = "*" +sniffio = "*" + +[package.extras] +brotli = ["brotli", "brotlicffi"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] + +[[package]] +name = "identify" +version = "2.6.1" +description = "File identification library for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "identify-2.6.1-py2.py3-none-any.whl", hash = "sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0"}, + {file = "identify-2.6.1.tar.gz", hash = "sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98"}, +] + +[package.extras] +license = ["ukkonen"] + +[[package]] +name = "idna" +version = "3.10" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.6" +files = [ + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, +] + +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[[package]] +name = "importlib-resources" +version = "6.4.5" +description = "Read resources from Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_resources-6.4.5-py3-none-any.whl", hash = "sha256:ac29d5f956f01d5e4bb63102a5a19957f1b9175e45649977264a1416783bb717"}, + {file = "importlib_resources-6.4.5.tar.gz", hash = "sha256:980862a1d16c9e147a59603677fa2aa5fd82b87f223b6cb870695bcfce830065"}, +] + +[package.dependencies] +zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["jaraco.test (>=5.4)", "pytest (>=6,!=8.1.*)", "zipp (>=3.17)"] +type = ["pytest-mypy"] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "itsdangerous" +version = "2.2.0" +description = "Safely pass data to untrusted environments and back." +optional = false +python-versions = ">=3.8" +files = [ + {file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"}, + {file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"}, +] + +[[package]] +name = "jinja2" +version = "3.1.4" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, + {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "limits" +version = "3.13.0" +description = "Rate limiting utilities" +optional = false +python-versions = ">=3.8" +files = [ + {file = "limits-3.13.0-py3-none-any.whl", hash = "sha256:9767f7233da4255e9904b79908a728e8ec0984c0b086058b4cbbd309aea553f6"}, + {file = "limits-3.13.0.tar.gz", hash = "sha256:6571b0c567bfa175a35fed9f8a954c0c92f1c3200804282f1b8f1de4ad98a953"}, +] + +[package.dependencies] +deprecated = ">=1.2" +importlib-resources = ">=1.3" +packaging = ">=21,<25" +typing-extensions = "*" + +[package.extras] +all = ["aetcd", "coredis (>=3.4.0,<5)", "emcache (>=0.6.1)", "emcache (>=1)", "etcd3", "motor (>=3,<4)", "pymemcache (>3,<5.0.0)", "pymongo (>4.1,<5)", "redis (>3,!=4.5.2,!=4.5.3,<6.0.0)", "redis (>=4.2.0,!=4.5.2,!=4.5.3)"] +async-etcd = ["aetcd"] +async-memcached = ["emcache (>=0.6.1)", "emcache (>=1)"] +async-mongodb = ["motor (>=3,<4)"] +async-redis = ["coredis (>=3.4.0,<5)"] +etcd = ["etcd3"] +memcached = ["pymemcache (>3,<5.0.0)"] +mongodb = ["pymongo (>4.1,<5)"] +redis = ["redis (>3,!=4.5.2,!=4.5.3,<6.0.0)"] +rediscluster = ["redis (>=4.2.0,!=4.5.2,!=4.5.3)"] + +[[package]] +name = "lnbits" +version = "1.0.0rc5" +description = "LNbits, free and open-source Lightning wallet and accounts system." +optional = false +python-versions = "<4.0,>=3.9" +files = [ + {file = "lnbits-1.0.0rc5-py3-none-any.whl", hash = "sha256:3107a9c6711720299add7880b62c020cbf34cee2486d47f2c3ddd3f6b4a76b9d"}, + {file = "lnbits-1.0.0rc5.tar.gz", hash = "sha256:f5559efa5d9b81f89acc1510db052d6300b3e93e1d0acdc70204689501865a51"}, +] + +[package.dependencies] +aiosqlite = "0.20.0" +asyncpg = "0.29.0" +bcrypt = "4.2.0" +bech32 = "1.2.0" +bolt11 = "2.1.0" +click = "8.1.7" +ecdsa = "0.19.0" +embit = "0.8.0" +environs = "9.5.0" +fastapi = "0.115.2" +fastapi-sso = "0.15.0" +grpcio = "1.66.1" +httpx = "0.27.0" +itsdangerous = "2.2.0" +jinja2 = "3.1.4" +lnurl = "0.5.3" +loguru = "0.7.2" +packaging = "24.0" +passlib = "1.7.4" +protobuf = "5.28.0" +pycryptodomex = "3.20.0" +pydantic = "1.10.18" +pyjwt = "2.9.0" +pyln-client = "24.8.1" +pyqrcode = "1.2.1" +python-crontab = "3.2.0" +pywebpush = "1.14.1" +secp256k1 = "0.14.0" +shortuuid = "1.0.13" +slowapi = "0.1.9" +sqlalchemy = "1.4.54" +sse-starlette = "1.8.2" +typing-extensions = "4.12.2" +uvicorn = "0.30.6" +uvloop = "0.19.0" +websocket-client = "1.8.0" +websockets = "11.0.3" + +[package.extras] +breez = ["breez-sdk (==0.5.2)"] +liquid = ["wallycore (==1.3.0)"] + +[[package]] +name = "lnurl" +version = "0.5.3" +description = "LNURL implementation for Python." +optional = false +python-versions = "<4.0,>=3.9" +files = [ + {file = "lnurl-0.5.3-py3-none-any.whl", hash = "sha256:feaf6c60b0b7f104894ef3accbd30d23d52e038c2797c58432baea7f4a8aa952"}, + {file = "lnurl-0.5.3.tar.gz", hash = "sha256:60154bcfdbb98fb143eeca970a16d73a582f28e057a826b5f222259411c497fe"}, +] + +[package.dependencies] +bech32 = ">=1.2.0,<2.0.0" +bolt11 = ">=2.0.5,<3.0.0" +ecdsa = ">=0.19.0,<0.20.0" +httpx = ">=0.27.0,<0.28.0" +pydantic = ">=1,<2" + +[[package]] +name = "loguru" +version = "0.7.2" +description = "Python logging made (stupidly) simple" +optional = false +python-versions = ">=3.5" +files = [ + {file = "loguru-0.7.2-py3-none-any.whl", hash = "sha256:003d71e3d3ed35f0f8984898359d65b79e5b21943f78af86aa5491210429b8eb"}, + {file = "loguru-0.7.2.tar.gz", hash = "sha256:e671a53522515f34fd406340ee968cb9ecafbc4b36c679da03c18fd8d0bd51ac"}, +] + +[package.dependencies] +colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""} +win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} + +[package.extras] +dev = ["Sphinx (==7.2.5)", "colorama (==0.4.5)", "colorama (==0.4.6)", "exceptiongroup (==1.1.3)", "freezegun (==1.1.0)", "freezegun (==1.2.2)", "mypy (==v0.910)", "mypy (==v0.971)", "mypy (==v1.4.1)", "mypy (==v1.5.1)", "pre-commit (==3.4.0)", "pytest (==6.1.2)", "pytest (==7.4.0)", "pytest-cov (==2.12.1)", "pytest-cov (==4.1.0)", "pytest-mypy-plugins (==1.9.3)", "pytest-mypy-plugins (==3.0.0)", "sphinx-autobuild (==2021.3.14)", "sphinx-rtd-theme (==1.3.0)", "tox (==3.27.1)", "tox (==4.11.0)"] + +[[package]] +name = "markupsafe" +version = "3.0.2" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.9" +files = [ + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"}, + {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, +] + +[[package]] +name = "marshmallow" +version = "3.23.1" +description = "A lightweight library for converting complex datatypes to and from native Python datatypes." +optional = false +python-versions = ">=3.9" +files = [ + {file = "marshmallow-3.23.1-py3-none-any.whl", hash = "sha256:fece2eb2c941180ea1b7fcbd4a83c51bfdd50093fdd3ad2585ee5e1df2508491"}, + {file = "marshmallow-3.23.1.tar.gz", hash = "sha256:3a8dfda6edd8dcdbf216c0ede1d1e78d230a6dc9c5a088f58c4083b974a0d468"}, +] + +[package.dependencies] +packaging = ">=17.0" + +[package.extras] +dev = ["marshmallow[tests]", "pre-commit (>=3.5,<5.0)", "tox"] +docs = ["alabaster (==1.0.0)", "autodocsumm (==0.2.14)", "sphinx (==8.1.3)", "sphinx-issues (==5.0.0)", "sphinx-version-warning (==1.1.2)"] +tests = ["pytest", "simplejson"] + +[[package]] +name = "mypy" +version = "1.13.0" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mypy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6607e0f1dd1fb7f0aca14d936d13fd19eba5e17e1cd2a14f808fa5f8f6d8f60a"}, + {file = "mypy-1.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8a21be69bd26fa81b1f80a61ee7ab05b076c674d9b18fb56239d72e21d9f4c80"}, + {file = "mypy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b2353a44d2179846a096e25691d54d59904559f4232519d420d64da6828a3a7"}, + {file = "mypy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0730d1c6a2739d4511dc4253f8274cdd140c55c32dfb0a4cf8b7a43f40abfa6f"}, + {file = "mypy-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5fc54dbb712ff5e5a0fca797e6e0aa25726c7e72c6a5850cfd2adbc1eb0a372"}, + {file = "mypy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d"}, + {file = "mypy-1.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d"}, + {file = "mypy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b"}, + {file = "mypy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73"}, + {file = "mypy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca"}, + {file = "mypy-1.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5"}, + {file = "mypy-1.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e"}, + {file = "mypy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2"}, + {file = "mypy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0"}, + {file = "mypy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2"}, + {file = "mypy-1.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7"}, + {file = "mypy-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62"}, + {file = "mypy-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8"}, + {file = "mypy-1.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7"}, + {file = "mypy-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc"}, + {file = "mypy-1.13.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:100fac22ce82925f676a734af0db922ecfea991e1d7ec0ceb1e115ebe501301a"}, + {file = "mypy-1.13.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7bcb0bb7f42a978bb323a7c88f1081d1b5dee77ca86f4100735a6f541299d8fb"}, + {file = "mypy-1.13.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bde31fc887c213e223bbfc34328070996061b0833b0a4cfec53745ed61f3519b"}, + {file = "mypy-1.13.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:07de989f89786f62b937851295ed62e51774722e5444a27cecca993fc3f9cd74"}, + {file = "mypy-1.13.0-cp38-cp38-win_amd64.whl", hash = "sha256:4bde84334fbe19bad704b3f5b78c4abd35ff1026f8ba72b29de70dda0916beb6"}, + {file = "mypy-1.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0246bcb1b5de7f08f2826451abd947bf656945209b140d16ed317f65a17dc7dc"}, + {file = "mypy-1.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7f5b7deae912cf8b77e990b9280f170381fdfbddf61b4ef80927edd813163732"}, + {file = "mypy-1.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7029881ec6ffb8bc233a4fa364736789582c738217b133f1b55967115288a2bc"}, + {file = "mypy-1.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3e38b980e5681f28f033f3be86b099a247b13c491f14bb8b1e1e134d23bb599d"}, + {file = "mypy-1.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:a6789be98a2017c912ae6ccb77ea553bbaf13d27605d2ca20a76dfbced631b24"}, + {file = "mypy-1.13.0-py3-none-any.whl", hash = "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a"}, + {file = "mypy-1.13.0.tar.gz", hash = "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e"}, +] + +[package.dependencies] +mypy-extensions = ">=1.0.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = ">=4.6.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +faster-cache = ["orjson"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "nodeenv" +version = "1.9.1" +description = "Node.js virtual environment builder" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, + {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, +] + +[[package]] +name = "oauthlib" +version = "3.2.2" +description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" +optional = false +python-versions = ">=3.6" +files = [ + {file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"}, + {file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"}, +] + +[package.extras] +rsa = ["cryptography (>=3.0.0)"] +signals = ["blinker (>=1.4.0)"] +signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] + +[[package]] +name = "packaging" +version = "24.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, +] + +[[package]] +name = "passlib" +version = "1.7.4" +description = "comprehensive password hashing framework supporting over 30 schemes" +optional = false +python-versions = "*" +files = [ + {file = "passlib-1.7.4-py2.py3-none-any.whl", hash = "sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1"}, + {file = "passlib-1.7.4.tar.gz", hash = "sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04"}, +] + +[package.extras] +argon2 = ["argon2-cffi (>=18.2.0)"] +bcrypt = ["bcrypt (>=3.1.0)"] +build-docs = ["cloud-sptheme (>=1.10.1)", "sphinx (>=1.6)", "sphinxcontrib-fulltoc (>=1.2.0)"] +totp = ["cryptography"] + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[[package]] +name = "platformdirs" +version = "4.3.6" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, + {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, +] + +[package.extras] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.11.2)"] + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pre-commit" +version = "3.8.0" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +optional = false +python-versions = ">=3.9" +files = [ + {file = "pre_commit-3.8.0-py2.py3-none-any.whl", hash = "sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f"}, + {file = "pre_commit-3.8.0.tar.gz", hash = "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af"}, +] + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +virtualenv = ">=20.10.0" + +[[package]] +name = "protobuf" +version = "5.28.0" +description = "" +optional = false +python-versions = ">=3.8" +files = [ + {file = "protobuf-5.28.0-cp310-abi3-win32.whl", hash = "sha256:66c3edeedb774a3508ae70d87b3a19786445fe9a068dd3585e0cefa8a77b83d0"}, + {file = "protobuf-5.28.0-cp310-abi3-win_amd64.whl", hash = "sha256:6d7cc9e60f976cf3e873acb9a40fed04afb5d224608ed5c1a105db4a3f09c5b6"}, + {file = "protobuf-5.28.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:532627e8fdd825cf8767a2d2b94d77e874d5ddb0adefb04b237f7cc296748681"}, + {file = "protobuf-5.28.0-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:018db9056b9d75eb93d12a9d35120f97a84d9a919bcab11ed56ad2d399d6e8dd"}, + {file = "protobuf-5.28.0-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:6206afcb2d90181ae8722798dcb56dc76675ab67458ac24c0dd7d75d632ac9bd"}, + {file = "protobuf-5.28.0-cp38-cp38-win32.whl", hash = "sha256:eef7a8a2f4318e2cb2dee8666d26e58eaf437c14788f3a2911d0c3da40405ae8"}, + {file = "protobuf-5.28.0-cp38-cp38-win_amd64.whl", hash = "sha256:d001a73c8bc2bf5b5c1360d59dd7573744e163b3607fa92788b7f3d5fefbd9a5"}, + {file = "protobuf-5.28.0-cp39-cp39-win32.whl", hash = "sha256:dde9fcaa24e7a9654f4baf2a55250b13a5ea701493d904c54069776b99a8216b"}, + {file = "protobuf-5.28.0-cp39-cp39-win_amd64.whl", hash = "sha256:853db610214e77ee817ecf0514e0d1d052dff7f63a0c157aa6eabae98db8a8de"}, + {file = "protobuf-5.28.0-py3-none-any.whl", hash = "sha256:510ed78cd0980f6d3218099e874714cdf0d8a95582e7b059b06cabad855ed0a0"}, + {file = "protobuf-5.28.0.tar.gz", hash = "sha256:dde74af0fa774fa98892209992295adbfb91da3fa98c8f67a88afe8f5a349add"}, +] + +[[package]] +name = "py-vapid" +version = "1.9.1" +description = "Simple VAPID header generation library" +optional = false +python-versions = "*" +files = [ + {file = "py_vapid-1.9.1.tar.gz", hash = "sha256:fe2b5461bf45c7baff1039df6981f03b87faa87cde0482addfa35b3fe636ac1b"}, +] + +[package.dependencies] +cryptography = ">=2.5" + +[[package]] +name = "pycparser" +version = "2.22" +description = "C parser in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, + {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, +] + +[[package]] +name = "pycryptodomex" +version = "3.20.0" +description = "Cryptographic library for Python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "pycryptodomex-3.20.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:645bd4ca6f543685d643dadf6a856cc382b654cc923460e3a10a49c1b3832aeb"}, + {file = "pycryptodomex-3.20.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ff5c9a67f8a4fba4aed887216e32cbc48f2a6fb2673bb10a99e43be463e15913"}, + {file = "pycryptodomex-3.20.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:8ee606964553c1a0bc74057dd8782a37d1c2bc0f01b83193b6f8bb14523b877b"}, + {file = "pycryptodomex-3.20.0-cp27-cp27m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7805830e0c56d88f4d491fa5ac640dfc894c5ec570d1ece6ed1546e9df2e98d6"}, + {file = "pycryptodomex-3.20.0-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:bc3ee1b4d97081260d92ae813a83de4d2653206967c4a0a017580f8b9548ddbc"}, + {file = "pycryptodomex-3.20.0-cp27-cp27m-win32.whl", hash = "sha256:8af1a451ff9e123d0d8bd5d5e60f8e3315c3a64f3cdd6bc853e26090e195cdc8"}, + {file = "pycryptodomex-3.20.0-cp27-cp27m-win_amd64.whl", hash = "sha256:cbe71b6712429650e3883dc81286edb94c328ffcd24849accac0a4dbcc76958a"}, + {file = "pycryptodomex-3.20.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:76bd15bb65c14900d98835fcd10f59e5e0435077431d3a394b60b15864fddd64"}, + {file = "pycryptodomex-3.20.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:653b29b0819605fe0898829c8ad6400a6ccde096146730c2da54eede9b7b8baa"}, + {file = "pycryptodomex-3.20.0-cp27-cp27mu-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62a5ec91388984909bb5398ea49ee61b68ecb579123694bffa172c3b0a107079"}, + {file = "pycryptodomex-3.20.0-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:108e5f1c1cd70ffce0b68739c75734437c919d2eaec8e85bffc2c8b4d2794305"}, + {file = "pycryptodomex-3.20.0-cp35-abi3-macosx_10_9_universal2.whl", hash = "sha256:59af01efb011b0e8b686ba7758d59cf4a8263f9ad35911bfe3f416cee4f5c08c"}, + {file = "pycryptodomex-3.20.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:82ee7696ed8eb9a82c7037f32ba9b7c59e51dda6f105b39f043b6ef293989cb3"}, + {file = "pycryptodomex-3.20.0-cp35-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91852d4480a4537d169c29a9d104dda44094c78f1f5b67bca76c29a91042b623"}, + {file = "pycryptodomex-3.20.0-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bca649483d5ed251d06daf25957f802e44e6bb6df2e8f218ae71968ff8f8edc4"}, + {file = "pycryptodomex-3.20.0-cp35-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e186342cfcc3aafaad565cbd496060e5a614b441cacc3995ef0091115c1f6c5"}, + {file = "pycryptodomex-3.20.0-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:25cd61e846aaab76d5791d006497134602a9e451e954833018161befc3b5b9ed"}, + {file = "pycryptodomex-3.20.0-cp35-abi3-musllinux_1_1_i686.whl", hash = "sha256:9c682436c359b5ada67e882fec34689726a09c461efd75b6ea77b2403d5665b7"}, + {file = "pycryptodomex-3.20.0-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:7a7a8f33a1f1fb762ede6cc9cbab8f2a9ba13b196bfaf7bc6f0b39d2ba315a43"}, + {file = "pycryptodomex-3.20.0-cp35-abi3-win32.whl", hash = "sha256:c39778fd0548d78917b61f03c1fa8bfda6cfcf98c767decf360945fe6f97461e"}, + {file = "pycryptodomex-3.20.0-cp35-abi3-win_amd64.whl", hash = "sha256:2a47bcc478741b71273b917232f521fd5704ab4b25d301669879e7273d3586cc"}, + {file = "pycryptodomex-3.20.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:1be97461c439a6af4fe1cf8bf6ca5936d3db252737d2f379cc6b2e394e12a458"}, + {file = "pycryptodomex-3.20.0-pp27-pypy_73-win32.whl", hash = "sha256:19764605feea0df966445d46533729b645033f134baeb3ea26ad518c9fdf212c"}, + {file = "pycryptodomex-3.20.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f2e497413560e03421484189a6b65e33fe800d3bd75590e6d78d4dfdb7accf3b"}, + {file = "pycryptodomex-3.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e48217c7901edd95f9f097feaa0388da215ed14ce2ece803d3f300b4e694abea"}, + {file = "pycryptodomex-3.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d00fe8596e1cc46b44bf3907354e9377aa030ec4cd04afbbf6e899fc1e2a7781"}, + {file = "pycryptodomex-3.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:88afd7a3af7ddddd42c2deda43d53d3dfc016c11327d0915f90ca34ebda91499"}, + {file = "pycryptodomex-3.20.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d3584623e68a5064a04748fb6d76117a21a7cb5eaba20608a41c7d0c61721794"}, + {file = "pycryptodomex-3.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0daad007b685db36d977f9de73f61f8da2a7104e20aca3effd30752fd56f73e1"}, + {file = "pycryptodomex-3.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5dcac11031a71348faaed1f403a0debd56bf5404232284cf8c761ff918886ebc"}, + {file = "pycryptodomex-3.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:69138068268127cd605e03438312d8f271135a33140e2742b417d027a0539427"}, + {file = "pycryptodomex-3.20.0.tar.gz", hash = "sha256:7a710b79baddd65b806402e14766c721aee8fb83381769c27920f26476276c1e"}, +] + +[[package]] +name = "pydantic" +version = "1.10.18" +description = "Data validation and settings management using python type hints" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pydantic-1.10.18-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e405ffcc1254d76bb0e760db101ee8916b620893e6edfbfee563b3c6f7a67c02"}, + {file = "pydantic-1.10.18-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e306e280ebebc65040034bff1a0a81fd86b2f4f05daac0131f29541cafd80b80"}, + {file = "pydantic-1.10.18-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11d9d9b87b50338b1b7de4ebf34fd29fdb0d219dc07ade29effc74d3d2609c62"}, + {file = "pydantic-1.10.18-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b661ce52c7b5e5f600c0c3c5839e71918346af2ef20062705ae76b5c16914cab"}, + {file = "pydantic-1.10.18-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c20f682defc9ef81cd7eaa485879ab29a86a0ba58acf669a78ed868e72bb89e0"}, + {file = "pydantic-1.10.18-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c5ae6b7c8483b1e0bf59e5f1843e4fd8fd405e11df7de217ee65b98eb5462861"}, + {file = "pydantic-1.10.18-cp310-cp310-win_amd64.whl", hash = "sha256:74fe19dda960b193b0eb82c1f4d2c8e5e26918d9cda858cbf3f41dd28549cb70"}, + {file = "pydantic-1.10.18-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:72fa46abace0a7743cc697dbb830a41ee84c9db8456e8d77a46d79b537efd7ec"}, + {file = "pydantic-1.10.18-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ef0fe7ad7cbdb5f372463d42e6ed4ca9c443a52ce544472d8842a0576d830da5"}, + {file = "pydantic-1.10.18-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a00e63104346145389b8e8f500bc6a241e729feaf0559b88b8aa513dd2065481"}, + {file = "pydantic-1.10.18-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae6fa2008e1443c46b7b3a5eb03800121868d5ab6bc7cda20b5df3e133cde8b3"}, + {file = "pydantic-1.10.18-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9f463abafdc92635da4b38807f5b9972276be7c8c5121989768549fceb8d2588"}, + {file = "pydantic-1.10.18-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3445426da503c7e40baccefb2b2989a0c5ce6b163679dd75f55493b460f05a8f"}, + {file = "pydantic-1.10.18-cp311-cp311-win_amd64.whl", hash = "sha256:467a14ee2183bc9c902579bb2f04c3d3dac00eff52e252850509a562255b2a33"}, + {file = "pydantic-1.10.18-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:efbc8a7f9cb5fe26122acba1852d8dcd1e125e723727c59dcd244da7bdaa54f2"}, + {file = "pydantic-1.10.18-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:24a4a159d0f7a8e26bf6463b0d3d60871d6a52eac5bb6a07a7df85c806f4c048"}, + {file = "pydantic-1.10.18-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b74be007703547dc52e3c37344d130a7bfacca7df112a9e5ceeb840a9ce195c7"}, + {file = "pydantic-1.10.18-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcb20d4cb355195c75000a49bb4a31d75e4295200df620f454bbc6bdf60ca890"}, + {file = "pydantic-1.10.18-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:46f379b8cb8a3585e3f61bf9ae7d606c70d133943f339d38b76e041ec234953f"}, + {file = "pydantic-1.10.18-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cbfbca662ed3729204090c4d09ee4beeecc1a7ecba5a159a94b5a4eb24e3759a"}, + {file = "pydantic-1.10.18-cp312-cp312-win_amd64.whl", hash = "sha256:c6d0a9f9eccaf7f438671a64acf654ef0d045466e63f9f68a579e2383b63f357"}, + {file = "pydantic-1.10.18-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3d5492dbf953d7d849751917e3b2433fb26010d977aa7a0765c37425a4026ff1"}, + {file = "pydantic-1.10.18-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe734914977eed33033b70bfc097e1baaffb589517863955430bf2e0846ac30f"}, + {file = "pydantic-1.10.18-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:15fdbe568beaca9aacfccd5ceadfb5f1a235087a127e8af5e48df9d8a45ae85c"}, + {file = "pydantic-1.10.18-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c3e742f62198c9eb9201781fbebe64533a3bbf6a76a91b8d438d62b813079dbc"}, + {file = "pydantic-1.10.18-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:19a3bd00b9dafc2cd7250d94d5b578edf7a0bd7daf102617153ff9a8fa37871c"}, + {file = "pydantic-1.10.18-cp37-cp37m-win_amd64.whl", hash = "sha256:2ce3fcf75b2bae99aa31bd4968de0474ebe8c8258a0110903478bd83dfee4e3b"}, + {file = "pydantic-1.10.18-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:335a32d72c51a313b33fa3a9b0fe283503272ef6467910338e123f90925f0f03"}, + {file = "pydantic-1.10.18-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:34a3613c7edb8c6fa578e58e9abe3c0f5e7430e0fc34a65a415a1683b9c32d9a"}, + {file = "pydantic-1.10.18-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9ee4e6ca1d9616797fa2e9c0bfb8815912c7d67aca96f77428e316741082a1b"}, + {file = "pydantic-1.10.18-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:23e8ec1ce4e57b4f441fc91e3c12adba023fedd06868445a5b5f1d48f0ab3682"}, + {file = "pydantic-1.10.18-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:44ae8a3e35a54d2e8fa88ed65e1b08967a9ef8c320819a969bfa09ce5528fafe"}, + {file = "pydantic-1.10.18-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5389eb3b48a72da28c6e061a247ab224381435256eb541e175798483368fdd3"}, + {file = "pydantic-1.10.18-cp38-cp38-win_amd64.whl", hash = "sha256:069b9c9fc645474d5ea3653788b544a9e0ccd3dca3ad8c900c4c6eac844b4620"}, + {file = "pydantic-1.10.18-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:80b982d42515632eb51f60fa1d217dfe0729f008e81a82d1544cc392e0a50ddf"}, + {file = "pydantic-1.10.18-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:aad8771ec8dbf9139b01b56f66386537c6fe4e76c8f7a47c10261b69ad25c2c9"}, + {file = "pydantic-1.10.18-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941a2eb0a1509bd7f31e355912eb33b698eb0051730b2eaf9e70e2e1589cae1d"}, + {file = "pydantic-1.10.18-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65f7361a09b07915a98efd17fdec23103307a54db2000bb92095457ca758d485"}, + {file = "pydantic-1.10.18-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6951f3f47cb5ca4da536ab161ac0163cab31417d20c54c6de5ddcab8bc813c3f"}, + {file = "pydantic-1.10.18-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7a4c5eec138a9b52c67f664c7d51d4c7234c5ad65dd8aacd919fb47445a62c86"}, + {file = "pydantic-1.10.18-cp39-cp39-win_amd64.whl", hash = "sha256:49e26c51ca854286bffc22b69787a8d4063a62bf7d83dc21d44d2ff426108518"}, + {file = "pydantic-1.10.18-py3-none-any.whl", hash = "sha256:06a189b81ffc52746ec9c8c007f16e5167c8b0a696e1a726369327e3db7b2a82"}, + {file = "pydantic-1.10.18.tar.gz", hash = "sha256:baebdff1907d1d96a139c25136a9bb7d17e118f133a76a2ef3b845e831e3403a"}, +] + +[package.dependencies] +email-validator = {version = ">=1.0.3", optional = true, markers = "extra == \"email\""} +typing-extensions = ">=4.2.0" + +[package.extras] +dotenv = ["python-dotenv (>=0.10.4)"] +email = ["email-validator (>=1.0.3)"] + +[[package]] +name = "pyjwt" +version = "2.9.0" +description = "JSON Web Token implementation in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "PyJWT-2.9.0-py3-none-any.whl", hash = "sha256:3b02fb0f44517787776cf48f2ae25d8e14f300e6d7545a4315cee571a415e850"}, + {file = "pyjwt-2.9.0.tar.gz", hash = "sha256:7e1e5b56cc735432a7369cbfa0efe50fa113ebecdc04ae6922deba8b84582d0c"}, +] + +[package.extras] +crypto = ["cryptography (>=3.4.0)"] +dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx", "sphinx-rtd-theme", "zope.interface"] +docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"] +tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] + +[[package]] +name = "pyln-bolt7" +version = "1.0.246" +description = "BOLT7" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "pyln-bolt7-1.0.246.tar.gz", hash = "sha256:2b53744fa21c1b12d2c9c9df153651b122e38fa65d4a5c3f2957317ee148e089"}, + {file = "pyln_bolt7-1.0.246-py3-none-any.whl", hash = "sha256:54d48ec27fdc8751762cb068b0a9f2757a58fb57933c6d8f8255d02c27eb63c5"}, +] + +[[package]] +name = "pyln-client" +version = "24.8.1" +description = "Client library and plugin library for Core Lightning" +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "pyln_client-24.8.1-py3-none-any.whl", hash = "sha256:4a389de96813a23b587007f181a5d4ba37bb8a4ac6bed7074a591b6b710e4615"}, + {file = "pyln_client-24.8.1.tar.gz", hash = "sha256:47e1bcadc91df511b7dbd66059d25a6c67fb0d49953291762272abf151fe8846"}, +] + +[package.dependencies] +pyln-bolt7 = ">=1.0" +pyln-proto = ">=23" + +[[package]] +name = "pyln-proto" +version = "24.8.2" +description = "This package implements some of the Lightning Network protocol in pure python. It is intended for protocol testing and some minor tooling only. It is not deemed secure enough to handle any amount of real funds (you have been warned!)." +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "pyln_proto-24.8.2-py3-none-any.whl", hash = "sha256:9c6c080c41fff40b119ea518fae37b1c8d4e917fa55389002afceffd4850ec98"}, + {file = "pyln_proto-24.8.2.tar.gz", hash = "sha256:efa222284e2990f7227f0243acc0e9ec5acd3bb89bb66ecad9f7dfb22b09fc90"}, +] + +[package.dependencies] +base58 = ">=2.1.1,<3.0.0" +bitstring = ">=4.1.0,<5.0.0" +coincurve = ">=20,<21" +cryptography = ">=42,<43" +PySocks = ">=1,<2" + +[[package]] +name = "pyqrcode" +version = "1.2.1" +description = "A QR code generator written purely in Python with SVG, EPS, PNG and terminal output." +optional = false +python-versions = "*" +files = [ + {file = "PyQRCode-1.2.1.tar.gz", hash = "sha256:fdbf7634733e56b72e27f9bce46e4550b75a3a2c420414035cae9d9d26b234d5"}, + {file = "PyQRCode-1.2.1.zip", hash = "sha256:1b2812775fa6ff5c527977c4cd2ccb07051ca7d0bc0aecf937a43864abe5eff6"}, +] + +[package.extras] +png = ["pypng (>=0.0.13)"] + +[[package]] +name = "pysocks" +version = "1.7.1" +description = "A Python SOCKS client module. See https://github.com/Anorov/PySocks for more information." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "PySocks-1.7.1-py27-none-any.whl", hash = "sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299"}, + {file = "PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5"}, + {file = "PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0"}, +] + +[[package]] +name = "pytest" +version = "7.4.4" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, + {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-asyncio" +version = "0.21.2" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest_asyncio-0.21.2-py3-none-any.whl", hash = "sha256:ab664c88bb7998f711d8039cacd4884da6430886ae8bbd4eded552ed2004f16b"}, + {file = "pytest_asyncio-0.21.2.tar.gz", hash = "sha256:d67738fc232b94b326b9d060750beb16e0074210b98dd8b58a5239fa2a154f45"}, +] + +[package.dependencies] +pytest = ">=7.0.0" + +[package.extras] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] +testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy (>=0.931)", "pytest-trio (>=0.7.0)"] + +[[package]] +name = "python-crontab" +version = "3.2.0" +description = "Python Crontab API" +optional = false +python-versions = "*" +files = [ + {file = "python_crontab-3.2.0-py3-none-any.whl", hash = "sha256:82cb9b6a312d41ff66fd3caf3eed7115c28c195bfb50711bc2b4b9592feb9fe5"}, + {file = "python_crontab-3.2.0.tar.gz", hash = "sha256:40067d1dd39ade3460b2ad8557c7651514cd3851deffff61c5c60e1227c5c36b"}, +] + +[package.dependencies] +python-dateutil = "*" + +[package.extras] +cron-description = ["cron-descriptor"] +cron-schedule = ["croniter"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "pywebpush" +version = "1.14.1" +description = "WebPush publication library" +optional = false +python-versions = "*" +files = [ + {file = "pywebpush-1.14.1-py3-none-any.whl", hash = "sha256:2865ee65cf44375f7cbdcfd5ba915a9d84c239900c6fba2245efd8d8314a3e84"}, + {file = "pywebpush-1.14.1.tar.gz", hash = "sha256:f88d7e2bf5e87c616dfb04b8c95c119238c511659b02f735ee77cc16842855ee"}, +] + +[package.dependencies] +cryptography = ">=2.6.1" +http-ece = ">=1.1.0" +py-vapid = ">=1.7.0" +requests = ">=2.21.0" +six = ">=1.15.0" + +[[package]] +name = "pyyaml" +version = "6.0.2" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, + {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, + {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, + {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, + {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, + {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, + {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, + {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, + {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, + {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, + {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, + {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, + {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, + {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, + {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, + {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, +] + +[[package]] +name = "requests" +version = "2.32.3" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.8" +files = [ + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "ruff" +version = "0.3.7" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.3.7-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:0e8377cccb2f07abd25e84fc5b2cbe48eeb0fea9f1719cad7caedb061d70e5ce"}, + {file = "ruff-0.3.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:15a4d1cc1e64e556fa0d67bfd388fed416b7f3b26d5d1c3e7d192c897e39ba4b"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d28bdf3d7dc71dd46929fafeec98ba89b7c3550c3f0978e36389b5631b793663"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:379b67d4f49774ba679593b232dcd90d9e10f04d96e3c8ce4a28037ae473f7bb"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c060aea8ad5ef21cdfbbe05475ab5104ce7827b639a78dd55383a6e9895b7c51"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:ebf8f615dde968272d70502c083ebf963b6781aacd3079081e03b32adfe4d58a"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d48098bd8f5c38897b03604f5428901b65e3c97d40b3952e38637b5404b739a2"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da8a4fda219bf9024692b1bc68c9cff4b80507879ada8769dc7e985755d662ea"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c44e0149f1d8b48c4d5c33d88c677a4aa22fd09b1683d6a7ff55b816b5d074f"}, + {file = "ruff-0.3.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3050ec0af72b709a62ecc2aca941b9cd479a7bf2b36cc4562f0033d688e44fa1"}, + {file = "ruff-0.3.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a29cc38e4c1ab00da18a3f6777f8b50099d73326981bb7d182e54a9a21bb4ff7"}, + {file = "ruff-0.3.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5b15cc59c19edca917f51b1956637db47e200b0fc5e6e1878233d3a938384b0b"}, + {file = "ruff-0.3.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e491045781b1e38b72c91247cf4634f040f8d0cb3e6d3d64d38dcf43616650b4"}, + {file = "ruff-0.3.7-py3-none-win32.whl", hash = "sha256:bc931de87593d64fad3a22e201e55ad76271f1d5bfc44e1a1887edd0903c7d9f"}, + {file = "ruff-0.3.7-py3-none-win_amd64.whl", hash = "sha256:5ef0e501e1e39f35e03c2acb1d1238c595b8bb36cf7a170e7c1df1b73da00e74"}, + {file = "ruff-0.3.7-py3-none-win_arm64.whl", hash = "sha256:789e144f6dc7019d1f92a812891c645274ed08af6037d11fc65fcbc183b7d59f"}, + {file = "ruff-0.3.7.tar.gz", hash = "sha256:d5c1aebee5162c2226784800ae031f660c350e7a3402c4d1f8ea4e97e232e3ba"}, +] + +[[package]] +name = "secp256k1" +version = "0.14.0" +description = "FFI bindings to libsecp256k1" +optional = false +python-versions = "*" +files = [ + {file = "secp256k1-0.14.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f666c67dcf1dc69e1448b2ede5e12aaf382b600204a61dbc65e4f82cea444405"}, + {file = "secp256k1-0.14.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fcabb3c3497a902fb61eec72d1b69bf72747d7bcc2a732d56d9319a1e8322262"}, + {file = "secp256k1-0.14.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7a27c479ab60571502516a1506a562d0a9df062de8ad645313fabfcc97252816"}, + {file = "secp256k1-0.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f4b9306bff6dde020444dfee9ca9b9f5b20ca53a2c0b04898361a3f43d5daf2e"}, + {file = "secp256k1-0.14.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:72735da6cb28273e924431cd40aa607e7f80ef09608c8c9300be2e0e1d2417b4"}, + {file = "secp256k1-0.14.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:87f4ad42a370f768910585989a301d1d65de17dcd86f6e8def9b021364b34d5c"}, + {file = "secp256k1-0.14.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:130f119b06142e597c10eb4470b5a38eae865362d01aaef06b113478d77f728d"}, + {file = "secp256k1-0.14.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:3aedcfe6eb1c5fa7c6be25b7cc91c76d8eb984271920ba0f7a934ae41ed56f51"}, + {file = "secp256k1-0.14.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c91dd3154f6c46ac798d9a41166120e1751222587f54516cc3f378f56ce4ac82"}, + {file = "secp256k1-0.14.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fec790cb6d0d37129ca0ce5b3f8e85692d5fb618d1c440f189453d18694035df"}, + {file = "secp256k1-0.14.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:63eb148196b8f646922d4be6739b17fbbf50ebb3a020078c823e2445d88b7a81"}, + {file = "secp256k1-0.14.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:adc23a4c5d24c95191638eb2ca313097827f07db102e77b59faed15d50c98cae"}, + {file = "secp256k1-0.14.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ce0314788d3248b275426501228969fd32f6501c9d1837902ee0e7bd8264a36f"}, + {file = "secp256k1-0.14.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:bc761894b3634021686714278fc62b73395fa3eded33453eadfd8a00a6c44ef3"}, + {file = "secp256k1-0.14.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:373dc8bca735f3c2d73259aa2711a9ecea2f3c7edbb663555fe3422e3dd76102"}, + {file = "secp256k1-0.14.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fe3f503c9dfdf663b500d3e0688ad842e116c2907ad3f1e1d685812df3f56290"}, + {file = "secp256k1-0.14.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4b1bf09953cde181132cf5e9033065615e5c2694e803165e2db763efa47695e5"}, + {file = "secp256k1-0.14.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6af07be5f8612628c3638dc7b208f6cc78d0abae3e25797eadb13890c7d5da81"}, + {file = "secp256k1-0.14.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a8dbd75a9fb6f42de307f3c5e24573fe59c3374637cbf39136edc66c200a4029"}, + {file = "secp256k1-0.14.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:97a30c8dae633cb18135c76b6517ae99dc59106818e8985be70dbc05dcc06c0d"}, + {file = "secp256k1-0.14.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f4062d8c101aa63b9ecb3709f1f075ad9c01b6672869bbaa1bd77271816936a7"}, + {file = "secp256k1-0.14.0-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c9e7c024ff17e9b9d7c392bb2a917da231d6cb40ab119389ff1f51dca10339a4"}, + {file = "secp256k1-0.14.0.tar.gz", hash = "sha256:82c06712d69ef945220c8b53c1a0d424c2ff6a1f64aee609030df79ad8383397"}, +] + +[package.dependencies] +cffi = ">=1.3.0" + +[[package]] +name = "shortuuid" +version = "1.0.13" +description = "A generator library for concise, unambiguous and URL-safe UUIDs." +optional = false +python-versions = ">=3.6" +files = [ + {file = "shortuuid-1.0.13-py3-none-any.whl", hash = "sha256:a482a497300b49b4953e15108a7913244e1bb0d41f9d332f5e9925dba33a3c5a"}, + {file = "shortuuid-1.0.13.tar.gz", hash = "sha256:3bb9cf07f606260584b1df46399c0b87dd84773e7b25912b7e391e30797c5e72"}, +] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "slowapi" +version = "0.1.9" +description = "A rate limiting extension for Starlette and Fastapi" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "slowapi-0.1.9-py3-none-any.whl", hash = "sha256:cfad116cfb84ad9d763ee155c1e5c5cbf00b0d47399a769b227865f5df576e36"}, + {file = "slowapi-0.1.9.tar.gz", hash = "sha256:639192d0f1ca01b1c6d95bf6c71d794c3a9ee189855337b4821f7f457dddad77"}, +] + +[package.dependencies] +limits = ">=2.3" + +[package.extras] +redis = ["redis (>=3.4.1,<4.0.0)"] + +[[package]] +name = "sniffio" +version = "1.3.1" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, +] + +[[package]] +name = "sqlalchemy" +version = "1.4.54" +description = "Database Abstraction Library" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "SQLAlchemy-1.4.54-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:af00236fe21c4d4f4c227b6ccc19b44c594160cc3ff28d104cdce85855369277"}, + {file = "SQLAlchemy-1.4.54-cp310-cp310-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1183599e25fa38a1a322294b949da02b4f0da13dbc2688ef9dbe746df573f8a6"}, + {file = "SQLAlchemy-1.4.54-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1990d5a6a5dc358a0894c8ca02043fb9a5ad9538422001fb2826e91c50f1d539"}, + {file = "SQLAlchemy-1.4.54-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:14b3f4783275339170984cadda66e3ec011cce87b405968dc8d51cf0f9997b0d"}, + {file = "SQLAlchemy-1.4.54-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b24364150738ce488333b3fb48bfa14c189a66de41cd632796fbcacb26b4585"}, + {file = "SQLAlchemy-1.4.54-cp310-cp310-win32.whl", hash = "sha256:a8a72259a1652f192c68377be7011eac3c463e9892ef2948828c7d58e4829988"}, + {file = "SQLAlchemy-1.4.54-cp310-cp310-win_amd64.whl", hash = "sha256:b67589f7955924865344e6eacfdcf70675e64f36800a576aa5e961f0008cde2a"}, + {file = "SQLAlchemy-1.4.54-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b05e0626ec1c391432eabb47a8abd3bf199fb74bfde7cc44a26d2b1b352c2c6e"}, + {file = "SQLAlchemy-1.4.54-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13e91d6892b5fcb94a36ba061fb7a1f03d0185ed9d8a77c84ba389e5bb05e936"}, + {file = "SQLAlchemy-1.4.54-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb59a11689ff3c58e7652260127f9e34f7f45478a2f3ef831ab6db7bcd72108f"}, + {file = "SQLAlchemy-1.4.54-cp311-cp311-win32.whl", hash = "sha256:1390ca2d301a2708fd4425c6d75528d22f26b8f5cbc9faba1ddca136671432bc"}, + {file = "SQLAlchemy-1.4.54-cp311-cp311-win_amd64.whl", hash = "sha256:2b37931eac4b837c45e2522066bda221ac6d80e78922fb77c75eb12e4dbcdee5"}, + {file = "SQLAlchemy-1.4.54-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:3f01c2629a7d6b30d8afe0326b8c649b74825a0e1ebdcb01e8ffd1c920deb07d"}, + {file = "SQLAlchemy-1.4.54-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c24dd161c06992ed16c5e528a75878edbaeced5660c3db88c820f1f0d3fe1f4"}, + {file = "SQLAlchemy-1.4.54-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5e0d47d619c739bdc636bbe007da4519fc953393304a5943e0b5aec96c9877c"}, + {file = "SQLAlchemy-1.4.54-cp312-cp312-win32.whl", hash = "sha256:12bc0141b245918b80d9d17eca94663dbd3f5266ac77a0be60750f36102bbb0f"}, + {file = "SQLAlchemy-1.4.54-cp312-cp312-win_amd64.whl", hash = "sha256:f941aaf15f47f316123e1933f9ea91a6efda73a161a6ab6046d1cde37be62c88"}, + {file = "SQLAlchemy-1.4.54-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:a41611835010ed4ea4c7aed1da5b58aac78ee7e70932a91ed2705a7b38e40f52"}, + {file = "SQLAlchemy-1.4.54-cp36-cp36m-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e8c1b9ecaf9f2590337d5622189aeb2f0dbc54ba0232fa0856cf390957584a9"}, + {file = "SQLAlchemy-1.4.54-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0de620f978ca273ce027769dc8db7e6ee72631796187adc8471b3c76091b809e"}, + {file = "SQLAlchemy-1.4.54-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c5a2530400a6e7e68fd1552a55515de6a4559122e495f73554a51cedafc11669"}, + {file = "SQLAlchemy-1.4.54-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0cf7076c8578b3de4e43a046cc7a1af8466e1c3f5e64167189fe8958a4f9c02"}, + {file = "SQLAlchemy-1.4.54-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:f1e1b92ee4ee9ffc68624ace218b89ca5ca667607ccee4541a90cc44999b9aea"}, + {file = "SQLAlchemy-1.4.54-cp37-cp37m-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41cffc63c7c83dfc30c4cab5b4308ba74440a9633c4509c51a0c52431fb0f8ab"}, + {file = "SQLAlchemy-1.4.54-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5933c45d11cbd9694b1540aa9076816cc7406964c7b16a380fd84d3a5fe3241"}, + {file = "SQLAlchemy-1.4.54-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cafe0ba3a96d0845121433cffa2b9232844a2609fce694fcc02f3f31214ece28"}, + {file = "SQLAlchemy-1.4.54-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a19f816f4702d7b1951d7576026c7124b9bfb64a9543e571774cf517b7a50b29"}, + {file = "SQLAlchemy-1.4.54-cp37-cp37m-win32.whl", hash = "sha256:76c2ba7b5a09863d0a8166fbc753af96d561818c572dbaf697c52095938e7be4"}, + {file = "SQLAlchemy-1.4.54-cp37-cp37m-win_amd64.whl", hash = "sha256:a86b0e4be775902a5496af4fb1b60d8a2a457d78f531458d294360b8637bb014"}, + {file = "SQLAlchemy-1.4.54-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:a49730afb716f3f675755afec109895cab95bc9875db7ffe2e42c1b1c6279482"}, + {file = "SQLAlchemy-1.4.54-cp38-cp38-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26e78444bc77d089e62874dc74df05a5c71f01ac598010a327881a48408d0064"}, + {file = "SQLAlchemy-1.4.54-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02d2ecb9508f16ab9c5af466dfe5a88e26adf2e1a8d1c56eb616396ccae2c186"}, + {file = "SQLAlchemy-1.4.54-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:394b0135900b62dbf63e4809cdc8ac923182af2816d06ea61cd6763943c2cc05"}, + {file = "SQLAlchemy-1.4.54-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ed3576675c187e3baa80b02c4c9d0edfab78eff4e89dd9da736b921333a2432"}, + {file = "SQLAlchemy-1.4.54-cp38-cp38-win32.whl", hash = "sha256:fc9ffd9a38e21fad3e8c5a88926d57f94a32546e937e0be46142b2702003eba7"}, + {file = "SQLAlchemy-1.4.54-cp38-cp38-win_amd64.whl", hash = "sha256:a01bc25eb7a5688656c8770f931d5cb4a44c7de1b3cec69b84cc9745d1e4cc10"}, + {file = "SQLAlchemy-1.4.54-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:0b76bbb1cbae618d10679be8966f6d66c94f301cfc15cb49e2f2382563fb6efb"}, + {file = "SQLAlchemy-1.4.54-cp39-cp39-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdb2886c0be2c6c54d0651d5a61c29ef347e8eec81fd83afebbf7b59b80b7393"}, + {file = "SQLAlchemy-1.4.54-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:954816850777ac234a4e32b8c88ac1f7847088a6e90cfb8f0e127a1bf3feddff"}, + {file = "SQLAlchemy-1.4.54-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1d83cd1cc03c22d922ec94d0d5f7b7c96b1332f5e122e81b1a61fb22da77879a"}, + {file = "SQLAlchemy-1.4.54-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1576fba3616f79496e2f067262200dbf4aab1bb727cd7e4e006076686413c80c"}, + {file = "SQLAlchemy-1.4.54-cp39-cp39-win32.whl", hash = "sha256:3112de9e11ff1957148c6de1df2bc5cc1440ee36783412e5eedc6f53638a577d"}, + {file = "SQLAlchemy-1.4.54-cp39-cp39-win_amd64.whl", hash = "sha256:6da60fb24577f989535b8fc8b2ddc4212204aaf02e53c4c7ac94ac364150ed08"}, + {file = "sqlalchemy-1.4.54.tar.gz", hash = "sha256:4470fbed088c35dc20b78a39aaf4ae54fe81790c783b3264872a0224f437c31a"}, +] + +[package.dependencies] +greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"} + +[package.extras] +aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] +aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] +asyncio = ["greenlet (!=0.4.17)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4)", "greenlet (!=0.4.17)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2)", "mariadb (>=1.0.1,!=1.1.2)"] +mssql = ["pyodbc"] +mssql-pymssql = ["pymssql", "pymssql"] +mssql-pyodbc = ["pyodbc", "pyodbc"] +mypy = ["mypy (>=0.910)", "sqlalchemy2-stubs"] +mysql = ["mysqlclient (>=1.4.0)", "mysqlclient (>=1.4.0,<2)"] +mysql-connector = ["mysql-connector-python", "mysql-connector-python"] +oracle = ["cx_oracle (>=7)", "cx_oracle (>=7,<8)"] +postgresql = ["psycopg2 (>=2.7)"] +postgresql-asyncpg = ["asyncpg", "asyncpg", "greenlet (!=0.4.17)", "greenlet (!=0.4.17)"] +postgresql-pg8000 = ["pg8000 (>=1.16.6,!=1.29.0)", "pg8000 (>=1.16.6,!=1.29.0)"] +postgresql-psycopg2binary = ["psycopg2-binary"] +postgresql-psycopg2cffi = ["psycopg2cffi"] +pymysql = ["pymysql", "pymysql (<1)"] +sqlcipher = ["sqlcipher3_binary"] + +[[package]] +name = "sse-starlette" +version = "1.8.2" +description = "SSE plugin for Starlette" +optional = false +python-versions = ">=3.8" +files = [ + {file = "sse_starlette-1.8.2-py3-none-any.whl", hash = "sha256:70cc7ef5aca4abe8a25dec1284cce4fe644dd7bf0c406d3e852e516092b7f849"}, + {file = "sse_starlette-1.8.2.tar.gz", hash = "sha256:e0f9b8dec41adc092a0a6e0694334bd3cfd3084c44c497a6ebc1fb4bdd919acd"}, +] + +[package.dependencies] +anyio = "*" +fastapi = "*" +starlette = "*" +uvicorn = "*" + +[[package]] +name = "starlette" +version = "0.40.0" +description = "The little ASGI library that shines." +optional = false +python-versions = ">=3.8" +files = [ + {file = "starlette-0.40.0-py3-none-any.whl", hash = "sha256:c494a22fae73805376ea6bf88439783ecfba9aac88a43911b48c653437e784c4"}, + {file = "starlette-0.40.0.tar.gz", hash = "sha256:1a3139688fb298ce5e2d661d37046a66ad996ce94be4d4983be019a23a04ea35"}, +] + +[package.dependencies] +anyio = ">=3.4.0,<5" +typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} + +[package.extras] +full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"] + +[[package]] +name = "tomli" +version = "2.0.2" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"}, + {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"}, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, +] + +[[package]] +name = "urllib3" +version = "2.2.3" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, + {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "uvicorn" +version = "0.30.6" +description = "The lightning-fast ASGI server." +optional = false +python-versions = ">=3.8" +files = [ + {file = "uvicorn-0.30.6-py3-none-any.whl", hash = "sha256:65fd46fe3fda5bdc1b03b94eb634923ff18cd35b2f084813ea79d1f103f711b5"}, + {file = "uvicorn-0.30.6.tar.gz", hash = "sha256:4b15decdda1e72be08209e860a1e10e92439ad5b97cf44cc945fcbee66fc5788"}, +] + +[package.dependencies] +click = ">=7.0" +h11 = ">=0.8" +typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} + +[package.extras] +standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] + +[[package]] +name = "uvloop" +version = "0.19.0" +description = "Fast implementation of asyncio event loop on top of libuv" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "uvloop-0.19.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de4313d7f575474c8f5a12e163f6d89c0a878bc49219641d49e6f1444369a90e"}, + {file = "uvloop-0.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5588bd21cf1fcf06bded085f37e43ce0e00424197e7c10e77afd4bbefffef428"}, + {file = "uvloop-0.19.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b1fd71c3843327f3bbc3237bedcdb6504fd50368ab3e04d0410e52ec293f5b8"}, + {file = "uvloop-0.19.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a05128d315e2912791de6088c34136bfcdd0c7cbc1cf85fd6fd1bb321b7c849"}, + {file = "uvloop-0.19.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:cd81bdc2b8219cb4b2556eea39d2e36bfa375a2dd021404f90a62e44efaaf957"}, + {file = "uvloop-0.19.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5f17766fb6da94135526273080f3455a112f82570b2ee5daa64d682387fe0dcd"}, + {file = "uvloop-0.19.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4ce6b0af8f2729a02a5d1575feacb2a94fc7b2e983868b009d51c9a9d2149bef"}, + {file = "uvloop-0.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:31e672bb38b45abc4f26e273be83b72a0d28d074d5b370fc4dcf4c4eb15417d2"}, + {file = "uvloop-0.19.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:570fc0ed613883d8d30ee40397b79207eedd2624891692471808a95069a007c1"}, + {file = "uvloop-0.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5138821e40b0c3e6c9478643b4660bd44372ae1e16a322b8fc07478f92684e24"}, + {file = "uvloop-0.19.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:91ab01c6cd00e39cde50173ba4ec68a1e578fee9279ba64f5221810a9e786533"}, + {file = "uvloop-0.19.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:47bf3e9312f63684efe283f7342afb414eea4d3011542155c7e625cd799c3b12"}, + {file = "uvloop-0.19.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:da8435a3bd498419ee8c13c34b89b5005130a476bda1d6ca8cfdde3de35cd650"}, + {file = "uvloop-0.19.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:02506dc23a5d90e04d4f65c7791e65cf44bd91b37f24cfc3ef6cf2aff05dc7ec"}, + {file = "uvloop-0.19.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2693049be9d36fef81741fddb3f441673ba12a34a704e7b4361efb75cf30befc"}, + {file = "uvloop-0.19.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7010271303961c6f0fe37731004335401eb9075a12680738731e9c92ddd96ad6"}, + {file = "uvloop-0.19.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5daa304d2161d2918fa9a17d5635099a2f78ae5b5960e742b2fcfbb7aefaa593"}, + {file = "uvloop-0.19.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7207272c9520203fea9b93843bb775d03e1cf88a80a936ce760f60bb5add92f3"}, + {file = "uvloop-0.19.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:78ab247f0b5671cc887c31d33f9b3abfb88d2614b84e4303f1a63b46c046c8bd"}, + {file = "uvloop-0.19.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:472d61143059c84947aa8bb74eabbace30d577a03a1805b77933d6bd13ddebbd"}, + {file = "uvloop-0.19.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45bf4c24c19fb8a50902ae37c5de50da81de4922af65baf760f7c0c42e1088be"}, + {file = "uvloop-0.19.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271718e26b3e17906b28b67314c45d19106112067205119dddbd834c2b7ce797"}, + {file = "uvloop-0.19.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:34175c9fd2a4bc3adc1380e1261f60306344e3407c20a4d684fd5f3be010fa3d"}, + {file = "uvloop-0.19.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e27f100e1ff17f6feeb1f33968bc185bf8ce41ca557deee9d9bbbffeb72030b7"}, + {file = "uvloop-0.19.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:13dfdf492af0aa0a0edf66807d2b465607d11c4fa48f4a1fd41cbea5b18e8e8b"}, + {file = "uvloop-0.19.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6e3d4e85ac060e2342ff85e90d0c04157acb210b9ce508e784a944f852a40e67"}, + {file = "uvloop-0.19.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ca4956c9ab567d87d59d49fa3704cf29e37109ad348f2d5223c9bf761a332e7"}, + {file = "uvloop-0.19.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f467a5fd23b4fc43ed86342641f3936a68ded707f4627622fa3f82a120e18256"}, + {file = "uvloop-0.19.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:492e2c32c2af3f971473bc22f086513cedfc66a130756145a931a90c3958cb17"}, + {file = "uvloop-0.19.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2df95fca285a9f5bfe730e51945ffe2fa71ccbfdde3b0da5772b4ee4f2e770d5"}, + {file = "uvloop-0.19.0.tar.gz", hash = "sha256:0246f4fd1bf2bf702e06b0d45ee91677ee5c31242f39aab4ea6fe0c51aedd0fd"}, +] + +[package.extras] +docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] +test = ["Cython (>=0.29.36,<0.30.0)", "aiohttp (==3.9.0b0)", "aiohttp (>=3.8.1)", "flake8 (>=5.0,<6.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=23.0.0,<23.1.0)", "pycodestyle (>=2.9.0,<2.10.0)"] + +[[package]] +name = "virtualenv" +version = "20.27.1" +description = "Virtual Python Environment builder" +optional = false +python-versions = ">=3.8" +files = [ + {file = "virtualenv-20.27.1-py3-none-any.whl", hash = "sha256:f11f1b8a29525562925f745563bfd48b189450f61fb34c4f9cc79dd5aa32a1f4"}, + {file = "virtualenv-20.27.1.tar.gz", hash = "sha256:142c6be10212543b32c6c45d3d3893dff89112cc588b7d0879ae5a1ec03a47ba"}, +] + +[package.dependencies] +distlib = ">=0.3.7,<1" +filelock = ">=3.12.2,<4" +platformdirs = ">=3.9.1,<5" + +[package.extras] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] + +[[package]] +name = "websocket-client" +version = "1.8.0" +description = "WebSocket client for Python with low level API options" +optional = false +python-versions = ">=3.8" +files = [ + {file = "websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526"}, + {file = "websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da"}, +] + +[package.extras] +docs = ["Sphinx (>=6.0)", "myst-parser (>=2.0.0)", "sphinx-rtd-theme (>=1.1.0)"] +optional = ["python-socks", "wsaccel"] +test = ["websockets"] + +[[package]] +name = "websockets" +version = "11.0.3" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "websockets-11.0.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3ccc8a0c387629aec40f2fc9fdcb4b9d5431954f934da3eaf16cdc94f67dbfac"}, + {file = "websockets-11.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d67ac60a307f760c6e65dad586f556dde58e683fab03323221a4e530ead6f74d"}, + {file = "websockets-11.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84d27a4832cc1a0ee07cdcf2b0629a8a72db73f4cf6de6f0904f6661227f256f"}, + {file = "websockets-11.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffd7dcaf744f25f82190856bc26ed81721508fc5cbf2a330751e135ff1283564"}, + {file = "websockets-11.0.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7622a89d696fc87af8e8d280d9b421db5133ef5b29d3f7a1ce9f1a7bf7fcfa11"}, + {file = "websockets-11.0.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bceab846bac555aff6427d060f2fcfff71042dba6f5fca7dc4f75cac815e57ca"}, + {file = "websockets-11.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:54c6e5b3d3a8936a4ab6870d46bdd6ec500ad62bde9e44462c32d18f1e9a8e54"}, + {file = "websockets-11.0.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:41f696ba95cd92dc047e46b41b26dd24518384749ed0d99bea0a941ca87404c4"}, + {file = "websockets-11.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:86d2a77fd490ae3ff6fae1c6ceaecad063d3cc2320b44377efdde79880e11526"}, + {file = "websockets-11.0.3-cp310-cp310-win32.whl", hash = "sha256:2d903ad4419f5b472de90cd2d40384573b25da71e33519a67797de17ef849b69"}, + {file = "websockets-11.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:1d2256283fa4b7f4c7d7d3e84dc2ece74d341bce57d5b9bf385df109c2a1a82f"}, + {file = "websockets-11.0.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e848f46a58b9fcf3d06061d17be388caf70ea5b8cc3466251963c8345e13f7eb"}, + {file = "websockets-11.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aa5003845cdd21ac0dc6c9bf661c5beddd01116f6eb9eb3c8e272353d45b3288"}, + {file = "websockets-11.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b58cbf0697721120866820b89f93659abc31c1e876bf20d0b3d03cef14faf84d"}, + {file = "websockets-11.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:660e2d9068d2bedc0912af508f30bbeb505bbbf9774d98def45f68278cea20d3"}, + {file = "websockets-11.0.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c1f0524f203e3bd35149f12157438f406eff2e4fb30f71221c8a5eceb3617b6b"}, + {file = "websockets-11.0.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:def07915168ac8f7853812cc593c71185a16216e9e4fa886358a17ed0fd9fcf6"}, + {file = "websockets-11.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b30c6590146e53149f04e85a6e4fcae068df4289e31e4aee1fdf56a0dead8f97"}, + {file = "websockets-11.0.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:619d9f06372b3a42bc29d0cd0354c9bb9fb39c2cbc1a9c5025b4538738dbffaf"}, + {file = "websockets-11.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:01f5567d9cf6f502d655151645d4e8b72b453413d3819d2b6f1185abc23e82dd"}, + {file = "websockets-11.0.3-cp311-cp311-win32.whl", hash = "sha256:e1459677e5d12be8bbc7584c35b992eea142911a6236a3278b9b5ce3326f282c"}, + {file = "websockets-11.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:e7837cb169eca3b3ae94cc5787c4fed99eef74c0ab9506756eea335e0d6f3ed8"}, + {file = "websockets-11.0.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:9f59a3c656fef341a99e3d63189852be7084c0e54b75734cde571182c087b152"}, + {file = "websockets-11.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2529338a6ff0eb0b50c7be33dc3d0e456381157a31eefc561771ee431134a97f"}, + {file = "websockets-11.0.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34fd59a4ac42dff6d4681d8843217137f6bc85ed29722f2f7222bd619d15e95b"}, + {file = "websockets-11.0.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:332d126167ddddec94597c2365537baf9ff62dfcc9db4266f263d455f2f031cb"}, + {file = "websockets-11.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6505c1b31274723ccaf5f515c1824a4ad2f0d191cec942666b3d0f3aa4cb4007"}, + {file = "websockets-11.0.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f467ba0050b7de85016b43f5a22b46383ef004c4f672148a8abf32bc999a87f0"}, + {file = "websockets-11.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:9d9acd80072abcc98bd2c86c3c9cd4ac2347b5a5a0cae7ed5c0ee5675f86d9af"}, + {file = "websockets-11.0.3-cp37-cp37m-win32.whl", hash = "sha256:e590228200fcfc7e9109509e4d9125eace2042fd52b595dd22bbc34bb282307f"}, + {file = "websockets-11.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:b16fff62b45eccb9c7abb18e60e7e446998093cdcb50fed33134b9b6878836de"}, + {file = "websockets-11.0.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fb06eea71a00a7af0ae6aefbb932fb8a7df3cb390cc217d51a9ad7343de1b8d0"}, + {file = "websockets-11.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8a34e13a62a59c871064dfd8ffb150867e54291e46d4a7cf11d02c94a5275bae"}, + {file = "websockets-11.0.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4841ed00f1026dfbced6fca7d963c4e7043aa832648671b5138008dc5a8f6d99"}, + {file = "websockets-11.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a073fc9ab1c8aff37c99f11f1641e16da517770e31a37265d2755282a5d28aa"}, + {file = "websockets-11.0.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:68b977f21ce443d6d378dbd5ca38621755f2063d6fdb3335bda981d552cfff86"}, + {file = "websockets-11.0.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1a99a7a71631f0efe727c10edfba09ea6bee4166a6f9c19aafb6c0b5917d09c"}, + {file = "websockets-11.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bee9fcb41db2a23bed96c6b6ead6489702c12334ea20a297aa095ce6d31370d0"}, + {file = "websockets-11.0.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4b253869ea05a5a073ebfdcb5cb3b0266a57c3764cf6fe114e4cd90f4bfa5f5e"}, + {file = "websockets-11.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:1553cb82942b2a74dd9b15a018dce645d4e68674de2ca31ff13ebc2d9f283788"}, + {file = "websockets-11.0.3-cp38-cp38-win32.whl", hash = "sha256:f61bdb1df43dc9c131791fbc2355535f9024b9a04398d3bd0684fc16ab07df74"}, + {file = "websockets-11.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:03aae4edc0b1c68498f41a6772d80ac7c1e33c06c6ffa2ac1c27a07653e79d6f"}, + {file = "websockets-11.0.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:777354ee16f02f643a4c7f2b3eff8027a33c9861edc691a2003531f5da4f6bc8"}, + {file = "websockets-11.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8c82f11964f010053e13daafdc7154ce7385ecc538989a354ccc7067fd7028fd"}, + {file = "websockets-11.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3580dd9c1ad0701169e4d6fc41e878ffe05e6bdcaf3c412f9d559389d0c9e016"}, + {file = "websockets-11.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f1a3f10f836fab6ca6efa97bb952300b20ae56b409414ca85bff2ad241d2a61"}, + {file = "websockets-11.0.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df41b9bc27c2c25b486bae7cf42fccdc52ff181c8c387bfd026624a491c2671b"}, + {file = "websockets-11.0.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:279e5de4671e79a9ac877427f4ac4ce93751b8823f276b681d04b2156713b9dd"}, + {file = "websockets-11.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1fdf26fa8a6a592f8f9235285b8affa72748dc12e964a5518c6c5e8f916716f7"}, + {file = "websockets-11.0.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:69269f3a0b472e91125b503d3c0b3566bda26da0a3261c49f0027eb6075086d1"}, + {file = "websockets-11.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:97b52894d948d2f6ea480171a27122d77af14ced35f62e5c892ca2fae9344311"}, + {file = "websockets-11.0.3-cp39-cp39-win32.whl", hash = "sha256:c7f3cb904cce8e1be667c7e6fef4516b98d1a6a0635a58a57528d577ac18a128"}, + {file = "websockets-11.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c792ea4eabc0159535608fc5658a74d1a81020eb35195dd63214dcf07556f67e"}, + {file = "websockets-11.0.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f2e58f2c36cc52d41f2659e4c0cbf7353e28c8c9e63e30d8c6d3494dc9fdedcf"}, + {file = "websockets-11.0.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de36fe9c02995c7e6ae6efe2e205816f5f00c22fd1fbf343d4d18c3d5ceac2f5"}, + {file = "websockets-11.0.3-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0ac56b661e60edd453585f4bd68eb6a29ae25b5184fd5ba51e97652580458998"}, + {file = "websockets-11.0.3-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e052b8467dd07d4943936009f46ae5ce7b908ddcac3fda581656b1b19c083d9b"}, + {file = "websockets-11.0.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:42cc5452a54a8e46a032521d7365da775823e21bfba2895fb7b77633cce031bb"}, + {file = "websockets-11.0.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e6316827e3e79b7b8e7d8e3b08f4e331af91a48e794d5d8b099928b6f0b85f20"}, + {file = "websockets-11.0.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8531fdcad636d82c517b26a448dcfe62f720e1922b33c81ce695d0edb91eb931"}, + {file = "websockets-11.0.3-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c114e8da9b475739dde229fd3bc6b05a6537a88a578358bc8eb29b4030fac9c9"}, + {file = "websockets-11.0.3-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e063b1865974611313a3849d43f2c3f5368093691349cf3c7c8f8f75ad7cb280"}, + {file = "websockets-11.0.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:92b2065d642bf8c0a82d59e59053dd2fdde64d4ed44efe4870fa816c1232647b"}, + {file = "websockets-11.0.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0ee68fe502f9031f19d495dae2c268830df2760c0524cbac5d759921ba8c8e82"}, + {file = "websockets-11.0.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcacf2c7a6c3a84e720d1bb2b543c675bf6c40e460300b628bab1b1efc7c034c"}, + {file = "websockets-11.0.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b67c6f5e5a401fc56394f191f00f9b3811fe843ee93f4a70df3c389d1adf857d"}, + {file = "websockets-11.0.3-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d5023a4b6a5b183dc838808087033ec5df77580485fc533e7dab2567851b0a4"}, + {file = "websockets-11.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ed058398f55163a79bb9f06a90ef9ccc063b204bb346c4de78efc5d15abfe602"}, + {file = "websockets-11.0.3-py3-none-any.whl", hash = "sha256:6681ba9e7f8f3b19440921e99efbb40fc89f26cd71bf539e45d8c8a25c976dc6"}, + {file = "websockets-11.0.3.tar.gz", hash = "sha256:88fc51d9a26b10fc331be344f1781224a375b78488fc343620184e95a4b27016"}, +] + +[[package]] +name = "win32-setctime" +version = "1.1.0" +description = "A small Python utility to set file creation time on Windows" +optional = false +python-versions = ">=3.5" +files = [ + {file = "win32_setctime-1.1.0-py3-none-any.whl", hash = "sha256:231db239e959c2fe7eb1d7dc129f11172354f98361c4fa2d6d2d7e278baa8aad"}, + {file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"}, +] + +[package.extras] +dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"] + +[[package]] +name = "wrapt" +version = "1.16.0" +description = "Module for decorators, wrappers and monkey patching." +optional = false +python-versions = ">=3.6" +files = [ + {file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"}, + {file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"}, + {file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"}, + {file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"}, + {file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"}, + {file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"}, + {file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"}, + {file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"}, + {file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"}, + {file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"}, + {file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"}, + {file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"}, + {file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"}, + {file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"}, + {file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"}, + {file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"}, + {file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"}, + {file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"}, + {file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"}, + {file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"}, + {file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"}, + {file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"}, + {file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"}, + {file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"}, + {file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"}, + {file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"}, + {file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"}, + {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"}, +] + +[[package]] +name = "zipp" +version = "3.20.2" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"}, + {file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"}, +] + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +type = ["pytest-mypy"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.10 | ^3.9" +content-hash = "3c44aa4c67a390622291459f117b591302ed643bf86e0953c9f3b6c31dc3d504" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..0d6f216 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,96 @@ +[tool.poetry] +name = "lnbits-nostrmarket" +version = "0.0.0" +description = "LNbits, free and open-source Lightning wallet and accounts system." +authors = ["Alan Bits "] + +[tool.poetry.dependencies] +python = "^3.10 | ^3.9" +lnbits = {version = "*", allow-prereleases = true} + +[tool.poetry.group.dev.dependencies] +black = "^24.3.0" +pytest-asyncio = "^0.21.0" +pytest = "^7.3.2" +mypy = "^1.5.1" +pre-commit = "^3.2.2" +ruff = "^0.3.2" + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" + +[tool.mypy] +exclude = "(nostr/*)" +[[tool.mypy.overrides]] +module = [ + "secp256k1.*", + "embit.*", + "lnbits.*", + "lnurl.*", + "loguru.*", + "fastapi.*", + "pydantic.*", + "pyqrcode.*", + "shortuuid.*", + "httpx.*", +] +ignore_missing_imports = "True" + +[tool.pytest.ini_options] +log_cli = false +testpaths = [ + "tests" +] + +[tool.black] +line-length = 88 + +[tool.ruff] +# Same as Black. + 10% rule of black +line-length = 88 +exclude = [ + "nostr", +] + +[tool.ruff.lint] +# Enable: +# F - pyflakes +# E - pycodestyle errors +# W - pycodestyle warnings +# I - isort +# A - flake8-builtins +# C - mccabe +# N - naming +# UP - pyupgrade +# RUF - ruff +# B - bugbear +select = ["F", "E", "W", "I", "A", "C", "N", "UP", "RUF", "B"] +ignore = ["C901"] + +# Allow autofix for all enabled rules (when `--fix`) is provided. +fixable = ["ALL"] +unfixable = [] + +# Allow unused variables when underscore-prefixed. +dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" + +# needed for pydantic +[tool.ruff.lint.pep8-naming] +classmethod-decorators = [ + "root_validator", +] + +# Ignore unused imports in __init__.py files. +# [tool.ruff.lint.extend-per-file-ignores] +# "__init__.py" = ["F401", "F403"] + +# [tool.ruff.lint.mccabe] +# max-complexity = 10 + +[tool.ruff.lint.flake8-bugbear] +# Allow default arguments like, e.g., `data: List[str] = fastapi.Query(None)`. +extend-immutable-calls = [ + "fastapi.Depends", + "fastapi.Query", +] diff --git a/services.py b/services.py index 55f68d9..a9b32ed 100644 --- a/services.py +++ b/services.py @@ -2,10 +2,10 @@ import json from typing import List, Optional, Tuple -from loguru import logger - from lnbits.bolt11 import decode -from lnbits.core.services import websocket_updater, create_invoice, get_wallet +from lnbits.core.crud import get_wallet +from lnbits.core.services import create_invoice, websocket_updater +from loguru import logger from . import nostr_client from .crud import ( @@ -75,7 +75,9 @@ async def create_new_order( await create_order(merchant.id, order) return PaymentRequest( - id=data.id, payment_options=[PaymentOption(type="ln", link=invoice)], message=receipt + id=data.id, + payment_options=[PaymentOption(type="ln", link=invoice)], + message=receipt, ) @@ -89,12 +91,11 @@ async def build_order_with_payment( shipping_zone = await get_zone(merchant_id, data.shipping_id) assert shipping_zone, f"Shipping zone not found for order '{data.id}'" + assert shipping_zone.id product_cost_sat, shipping_cost_sat = await data.costs_in_sats( products, shipping_zone.id, shipping_zone.cost ) - receipt = data.receipt( - products, shipping_zone.id, shipping_zone.cost - ) + receipt = data.receipt(products, shipping_zone.id, shipping_zone.cost) wallet_id = await get_wallet_for_product(data.items[0].product_id) assert wallet_id, "Missing wallet for order `{data.id}`" @@ -106,7 +107,7 @@ async def build_order_with_payment( if not success: raise ValueError(message) - payment_hash, invoice = await create_invoice( + payment = await create_invoice( wallet_id=wallet_id, amount=round(product_cost_sat + shipping_cost_sat), memo=f"Order '{data.id}' for pubkey '{data.public_key}'", @@ -124,19 +125,21 @@ async def build_order_with_payment( order = Order( **data.dict(), stall_id=products[0].stall_id, - invoice_id=payment_hash, + invoice_id=payment.payment_hash, total=product_cost_sat + shipping_cost_sat, extra=extra, ) - return order, invoice, receipt + return order, payment.bolt11, receipt async def update_merchant_to_nostr( merchant: Merchant, delete_merchant=False ) -> Merchant: stalls = await get_stalls(merchant.id) + event: Optional[NostrEvent] = None for stall in stalls: + assert stall.id products = await get_products(merchant.id, stall.id) for product in products: event = await sign_and_send_to_nostr(merchant, product, delete_merchant) @@ -150,6 +153,7 @@ async def update_merchant_to_nostr( if delete_merchant: # merchant profile updates not supported yet event = await sign_and_send_to_nostr(merchant, merchant, delete_merchant) + assert event merchant.config.event_id = event.id return merchant @@ -227,25 +231,29 @@ async def update_products_for_order( return success, message for p in products: + assert p.id product = await update_product_quantity(p.id, p.quantity) + assert product event = await sign_and_send_to_nostr(merchant, product) product.event_id = event.id await update_product(merchant.id, product) return True, "ok" -async def autoreply_for_products_in_order( - merchant: Merchant, order: Order -) -> Tuple[bool, str]: + +async def autoreply_for_products_in_order(merchant: Merchant, order: Order): product_ids = [i.product_id for i in order.items] products = await get_products_by_ids(merchant.id, product_ids) products_with_autoreply = [p for p in products if p.config.use_autoreply] for p in products_with_autoreply: - dm_content = p.config.autoreply_message + dm_content = p.config.autoreply_message or "" await send_dm( - merchant, order.public_key, DirectMessageType.PLAIN_TEXT.value, dm_content + merchant, + order.public_key, + DirectMessageType.PLAIN_TEXT.value, + dm_content, ) await asyncio.sleep(1) # do not send all autoreplies at once @@ -253,7 +261,7 @@ async def autoreply_for_products_in_order( async def send_dm( merchant: Merchant, other_pubkey: str, - type: str, + type_: int, dm_content: str, ): dm_event = merchant.build_dm_event(dm_content, other_pubkey) @@ -263,7 +271,7 @@ async def send_dm( event_created_at=dm_event.created_at, message=dm_content, public_key=other_pubkey, - type=type, + type=type_, ) dm_reply = await create_direct_message(merchant.id, dm) @@ -296,7 +304,8 @@ async def compute_products_new_quantity( return ( False, [], - f"Quantity not sufficient for product: '{p.name}' ({p.id}). Required '{required_quantity}' but only have '{p.quantity}'.", + f"Quantity not sufficient for product: '{p.name}' ({p.id})." + f" Required '{required_quantity}' but only have '{p.quantity}'.", ) p.quantity -= required_quantity @@ -306,9 +315,9 @@ async def compute_products_new_quantity( async def process_nostr_message(msg: str): try: - type, *rest = json.loads(msg) + type_, *rest = json.loads(msg) - if type.upper() == "EVENT": + if type_.upper() == "EVENT": _, event = rest event = NostrEvent(**event) if event.kind == 0: @@ -328,11 +337,11 @@ async def process_nostr_message(msg: str): async def create_or_update_order_from_dm( merchant_id: str, merchant_pubkey: str, dm: DirectMessage ): - type, json_data = PartialDirectMessage.parse_message(dm.message) - if "id" not in json_data: + type_, json_data = PartialDirectMessage.parse_message(dm.message) + if not json_data or "id" not in json_data: return - if type == DirectMessageType.CUSTOMER_ORDER: + if type_ == DirectMessageType.CUSTOMER_ORDER: order = await extract_customer_order_from_dm( merchant_id, merchant_pubkey, dm, json_data ) @@ -348,7 +357,7 @@ async def create_or_update_order_from_dm( ) return - if type == DirectMessageType.PAYMENT_REQUEST: + if type_ == DirectMessageType.PAYMENT_REQUEST: payment_request = PaymentRequest(**json_data) pr = next( (o.link for o in payment_request.payment_options if o.type == "ln"), None @@ -356,14 +365,15 @@ async def create_or_update_order_from_dm( if not pr: return invoice = decode(pr) + total = invoice.amount_msat / 1000 if invoice.amount_msat else 0 await update_order( merchant_id, payment_request.id, - **{"total": invoice.amount_msat / 1000, "invoice_id": invoice.payment_hash}, + **{"total": total, "invoice_id": invoice.payment_hash}, ) return - if type == DirectMessageType.ORDER_PAID_OR_SHIPPED: + if type_ == DirectMessageType.ORDER_PAID_OR_SHIPPED: order_update = OrderStatusUpdate(**json_data) if order_update.paid: await update_order_paid_status(order_update.id, True) @@ -380,16 +390,18 @@ async def extract_customer_order_from_dm( ) extra = await OrderExtra.from_products(products) order = Order( - id=json_data.get("id"), + id=str(json_data.get("id")), event_id=dm.event_id, event_created_at=dm.event_created_at, public_key=dm.public_key, merchant_public_key=merchant_pubkey, shipping_id=json_data.get("shipping_id", "None"), items=order_items, - contact=OrderContact(**json_data.get("contact")) - if json_data.get("contact") - else None, + contact=( + OrderContact(**json_data.get("contact", {})) + if json_data.get("contact") + else None + ), address=json_data.get("address"), stall_id=products[0].stall_id if len(products) else "None", invoice_id="None", @@ -406,12 +418,9 @@ async def _handle_nip04_message(event: NostrEvent): if not merchant: p_tags = event.tag_values("p") - merchant_public_key = p_tags[0] if len(p_tags) else None - merchant = ( - await get_merchant_by_pubkey(merchant_public_key) - if merchant_public_key - else None - ) + if len(p_tags) and p_tags[0]: + merchant_public_key = p_tags[0] + merchant = await get_merchant_by_pubkey(merchant_public_key) assert merchant, f"Merchant not found for public key '{merchant_public_key}'" @@ -461,21 +470,21 @@ async def _handle_outgoing_dms( event: NostrEvent, merchant: Merchant, clear_text_msg: str ): sent_to = event.tag_values("p") - type, _ = PartialDirectMessage.parse_message(clear_text_msg) + type_, _ = PartialDirectMessage.parse_message(clear_text_msg) if len(sent_to) != 0: dm = PartialDirectMessage( event_id=event.id, event_created_at=event.created_at, message=clear_text_msg, public_key=sent_to[0], - type=type.value, + type=type_.value, ) await create_direct_message(merchant.id, dm) async def _handle_incoming_structured_dm( merchant: Merchant, dm: DirectMessage, json_data: dict -) -> Tuple[DirectMessageType, str]: +) -> Tuple[DirectMessageType, Optional[str]]: try: if dm.type == DirectMessageType.CUSTOMER_ORDER.value and merchant.config.active: json_resp = await _handle_new_order( @@ -487,7 +496,7 @@ async def _handle_incoming_structured_dm( except Exception as ex: logger.warning(ex) - return None, None + return DirectMessageType.PLAIN_TEXT, None async def _persist_dm( @@ -570,9 +579,13 @@ async def _handle_new_order( except Exception as e: logger.debug(e) payment_req = await create_new_failed_order( - merchant_id, merchant_public_key, dm, json_data, "Order received, but cannot be processed. Please contact merchant." + merchant_id, + merchant_public_key, + dm, + json_data, + "Order received, but cannot be processed. Please contact merchant.", ) - + assert payment_req response = { "type": DirectMessageType.PAYMENT_REQUEST.value, **payment_req.dict(), @@ -594,12 +607,14 @@ async def create_new_failed_order( await create_order(merchant_id, order) return PaymentRequest(id=order.id, message=fail_message, payment_options=[]) + async def resubscribe_to_all_merchants(): await nostr_client.unsubscribe_merchants() # give some time for the message to propagate - asyncio.sleep(1) + await asyncio.sleep(1) await subscribe_to_all_merchants() + async def subscribe_to_all_merchants(): ids = await get_merchants_ids_with_pubkeys() public_keys = [pk for _, pk in ids] @@ -608,7 +623,9 @@ async def subscribe_to_all_merchants(): last_stall_time = await get_last_stall_update_time() last_prod_time = await get_last_product_update_time() - await nostr_client.subscribe_merchants(public_keys, last_dm_time, last_stall_time, last_prod_time, 0) + await nostr_client.subscribe_merchants( + public_keys, last_dm_time, last_stall_time, last_prod_time, 0 + ) async def _handle_new_customer(event: NostrEvent, merchant: Merchant): diff --git a/static/components/direct-messages.js b/static/components/direct-messages.js new file mode 100644 index 0000000..b9dee13 --- /dev/null +++ b/static/components/direct-messages.js @@ -0,0 +1,170 @@ +window.app.component('direct-messages', { + name: 'direct-messages', + props: ['active-chat-customer', 'merchant-id', 'adminkey', 'inkey'], + template: '#direct-messages', + delimiters: ['${', '}'], + watch: { + activeChatCustomer: async function (n) { + this.activePublicKey = n + }, + activePublicKey: async function (n) { + await this.getDirectMessages(n) + } + }, + computed: { + messagesAsJson: function () { + return this.messages.map(m => { + const dateFrom = moment(m.event_created_at * 1000).fromNow() + try { + const message = JSON.parse(m.message) + return { + isJson: message.type >= 0, + dateFrom, + ...m, + message + } + } catch (error) { + return { + isJson: false, + dateFrom, + ...m, + message: m.message + } + } + }) + } + }, + data: function () { + return { + customers: [], + unreadMessages: 0, + activePublicKey: null, + messages: [], + newMessage: '', + showAddPublicKey: false, + newPublicKey: null, + showRawMessage: false, + rawMessage: null + } + }, + methods: { + sendMessage: async function () {}, + buildCustomerLabel: function (c) { + let label = `${c.profile.name || 'unknown'} ${c.profile.about || ''}` + if (c.unread_messages) { + label += `[new: ${c.unread_messages}]` + } + label += ` (${c.public_key.slice(0, 16)}...${c.public_key.slice( + c.public_key.length - 16 + )}` + return label + }, + getDirectMessages: async function (pubkey) { + if (!pubkey) { + this.messages = [] + return + } + try { + const {data} = await LNbits.api.request( + 'GET', + '/nostrmarket/api/v1/message/' + pubkey, + this.inkey + ) + this.messages = data + + this.focusOnChatBox(this.messages.length - 1) + } catch (error) { + LNbits.utils.notifyApiError(error) + } + }, + getCustomers: async function () { + try { + const {data} = await LNbits.api.request( + 'GET', + '/nostrmarket/api/v1/customer', + this.inkey + ) + this.customers = data + this.unreadMessages = data.filter(c => c.unread_messages).length + } catch (error) { + LNbits.utils.notifyApiError(error) + } + }, + + sendDirectMesage: async function () { + try { + const {data} = await LNbits.api.request( + 'POST', + '/nostrmarket/api/v1/message', + this.adminkey, + { + message: this.newMessage, + public_key: this.activePublicKey + } + ) + this.messages = this.messages.concat([data]) + this.newMessage = '' + this.focusOnChatBox(this.messages.length - 1) + } catch (error) { + LNbits.utils.notifyApiError(error) + } + }, + addPublicKey: async function () { + try { + const {data} = await LNbits.api.request( + 'POST', + '/nostrmarket/api/v1/customer', + this.adminkey, + { + public_key: this.newPublicKey, + merchant_id: this.merchantId, + unread_messages: 0 + } + ) + this.newPublicKey = null + this.activePublicKey = data.public_key + await this.selectActiveCustomer() + } catch (error) { + LNbits.utils.notifyApiError(error) + } finally { + this.showAddPublicKey = false + } + }, + handleNewMessage: async function (data) { + if (data.customerPubkey === this.activePublicKey) { + this.messages.push(data.dm) + this.focusOnChatBox(this.messages.length - 1) + // focus back on input box + } + this.getCustomersDebounced() + }, + showOrderDetails: function (orderId, eventId) { + this.$emit('order-selected', {orderId, eventId}) + }, + showClientOrders: function () { + this.$emit('customer-selected', this.activePublicKey) + }, + selectActiveCustomer: async function () { + await this.getDirectMessages(this.activePublicKey) + await this.getCustomers() + }, + showMessageRawData: function (index) { + this.rawMessage = this.messages[index]?.message + this.showRawMessage = true + }, + focusOnChatBox: function (index) { + setTimeout(() => { + const lastChatBox = document.getElementsByClassName( + `chat-mesage-index-${index}` + ) + if (lastChatBox && lastChatBox[0]) { + lastChatBox[0].scrollIntoView() + } + }, 100) + } + }, + created: async function () { + await this.getCustomers() + this.getCustomersDebounced = _.debounce(this.getCustomers, 2000, false) + } +}) diff --git a/static/components/direct-messages/direct-messages.html b/static/components/direct-messages/direct-messages.html deleted file mode 100644 index b6b90d6..0000000 --- a/static/components/direct-messages/direct-messages.html +++ /dev/null @@ -1,104 +0,0 @@ -
- - -
-
-
Messages
-
-
-   new -
-
- Client - Orders -
-
-
- - - - -
-
- - -
-
- - - Add a public key to chat with - - -
-
- -
- -
-
-
- -
-
- New order: -
-
- Reply sent for order: -
-
- Paid - Shipped -
-
- - - - -
- ... - -
-
-
-
- - - - - - - -
-
-
-
- - - - -
- Add - Cancel -
-
-
-
- - - -
- - Close -
- -
-
-
-
\ No newline at end of file diff --git a/static/components/direct-messages/direct-messages.js b/static/components/direct-messages/direct-messages.js deleted file mode 100644 index 607342a..0000000 --- a/static/components/direct-messages/direct-messages.js +++ /dev/null @@ -1,173 +0,0 @@ -async function directMessages(path) { - const template = await loadTemplateAsync(path) - Vue.component('direct-messages', { - name: 'direct-messages', - props: ['active-chat-customer', 'merchant-id', 'adminkey', 'inkey'], - template, - - watch: { - activeChatCustomer: async function (n) { - this.activePublicKey = n - }, - activePublicKey: async function (n) { - await this.getDirectMessages(n) - } - }, - computed: { - messagesAsJson: function () { - return this.messages.map(m => { - const dateFrom = moment(m.event_created_at * 1000).fromNow() - try { - const message = JSON.parse(m.message) - return { - isJson: message.type >= 0, - dateFrom, - ...m, - message - } - } catch (error) { - return { - isJson: false, - dateFrom, - ...m, - message: m.message - } - } - }) - } - }, - data: function () { - return { - customers: [], - unreadMessages: 0, - activePublicKey: null, - messages: [], - newMessage: '', - showAddPublicKey: false, - newPublicKey: null, - showRawMessage: false, - rawMessage: null, - } - }, - methods: { - sendMessage: async function () { }, - buildCustomerLabel: function (c) { - let label = `${c.profile.name || 'unknown'} ${c.profile.about || ''}` - if (c.unread_messages) { - label += `[new: ${c.unread_messages}]` - } - label += ` (${c.public_key.slice(0, 16)}...${c.public_key.slice( - c.public_key.length - 16 - )}` - return label - }, - getDirectMessages: async function (pubkey) { - if (!pubkey) { - this.messages = [] - return - } - try { - const { data } = await LNbits.api.request( - 'GET', - '/nostrmarket/api/v1/message/' + pubkey, - this.inkey - ) - this.messages = data - - this.focusOnChatBox(this.messages.length - 1) - } catch (error) { - LNbits.utils.notifyApiError(error) - } - }, - getCustomers: async function () { - try { - const { data } = await LNbits.api.request( - 'GET', - '/nostrmarket/api/v1/customer', - this.inkey - ) - this.customers = data - this.unreadMessages = data.filter(c => c.unread_messages).length - } catch (error) { - LNbits.utils.notifyApiError(error) - } - }, - - sendDirectMesage: async function () { - try { - const { data } = await LNbits.api.request( - 'POST', - '/nostrmarket/api/v1/message', - this.adminkey, - { - message: this.newMessage, - public_key: this.activePublicKey - } - ) - this.messages = this.messages.concat([data]) - this.newMessage = '' - this.focusOnChatBox(this.messages.length - 1) - } catch (error) { - LNbits.utils.notifyApiError(error) - } - }, - addPublicKey: async function () { - try { - const { data } = await LNbits.api.request( - 'POST', - '/nostrmarket/api/v1/customer', - this.adminkey, - { - public_key: this.newPublicKey, - merchant_id: this.merchantId, - unread_messages: 0 - } - ) - this.newPublicKey = null - this.activePublicKey = data.public_key - await this.selectActiveCustomer() - } catch (error) { - LNbits.utils.notifyApiError(error) - } finally { - this.showAddPublicKey = false - } - }, - handleNewMessage: async function (data) { - if (data.customerPubkey === this.activePublicKey) { - this.messages.push(data.dm) - this.focusOnChatBox(this.messages.length - 1) - // focus back on input box - } - this.getCustomersDebounced() - }, - showOrderDetails: function (orderId, eventId) { - this.$emit('order-selected', { orderId, eventId }) - }, - showClientOrders: function () { - this.$emit('customer-selected', this.activePublicKey) - }, - selectActiveCustomer: async function () { - await this.getDirectMessages(this.activePublicKey) - await this.getCustomers() - }, - showMessageRawData: function (index) { - this.rawMessage = this.messages[index]?.message - this.showRawMessage = true - }, - focusOnChatBox: function (index) { - setTimeout(() => { - const lastChatBox = document.getElementsByClassName( - `chat-mesage-index-${index}` - ) - if (lastChatBox && lastChatBox[0]) { - lastChatBox[0].scrollIntoView() - } - }, 100) - } - }, - created: async function () { - await this.getCustomers() - this.getCustomersDebounced = _.debounce(this.getCustomers, 2000, false) - } - }) -} diff --git a/static/components/key-pair.js b/static/components/key-pair.js new file mode 100644 index 0000000..5bf9d23 --- /dev/null +++ b/static/components/key-pair.js @@ -0,0 +1,22 @@ +window.app.component('key-pair', { + name: 'key-pair', + template: '#key-pair', + delimiters: ['${', '}'], + props: ['public-key', 'private-key'], + data: function () { + return { + showPrivateKey: false + } + }, + methods: { + copyText: function (text, message, position) { + var notify = this.$q.notify + Quasar.copyToClipboard(text).then(function () { + notify({ + message: message || 'Copied to clipboard!', + position: position || 'bottom' + }) + }) + } + } +}) diff --git a/static/components/key-pair/key-pair.js b/static/components/key-pair/key-pair.js deleted file mode 100644 index bee16b4..0000000 --- a/static/components/key-pair/key-pair.js +++ /dev/null @@ -1,25 +0,0 @@ -async function keyPair(path) { - const template = await loadTemplateAsync(path) - Vue.component('key-pair', { - name: 'key-pair', - template, - - props: ['public-key', 'private-key'], - data: function () { - return { - showPrivateKey: false - } - }, - methods: { - copyText: function (text, message, position) { - var notify = this.$q.notify - Quasar.utils.copyToClipboard(text).then(function () { - notify({ - message: message || 'Copied to clipboard!', - position: position || 'bottom' - }) - }) - } - } - }) -} diff --git a/static/components/merchant-details.js b/static/components/merchant-details.js new file mode 100644 index 0000000..4581b4e --- /dev/null +++ b/static/components/merchant-details.js @@ -0,0 +1,102 @@ +window.app.component('merchant-details', { + name: 'merchant-details', + template: '#merchant-details', + props: ['merchant-id', 'adminkey', 'inkey', 'showKeys'], + delimiters: ['${', '}'], + data: function () { + return {} + }, + methods: { + toggleShowKeys: async function () { + this.$emit('toggle-show-keys') + }, + + republishMerchantData: async function () { + try { + await LNbits.api.request( + 'PUT', + `/nostrmarket/api/v1/merchant/${this.merchantId}/nostr`, + this.adminkey + ) + this.$q.notify({ + type: 'positive', + message: 'Merchant data republished to Nostr', + timeout: 5000 + }) + } catch (error) { + console.warn(error) + LNbits.utils.notifyApiError(error) + } + }, + requeryMerchantData: async function () { + try { + await LNbits.api.request( + 'GET', + `/nostrmarket/api/v1/merchant/${this.merchantId}/nostr`, + this.adminkey + ) + this.$q.notify({ + type: 'positive', + message: 'Merchant data refreshed from Nostr', + timeout: 5000 + }) + } catch (error) { + console.warn(error) + LNbits.utils.notifyApiError(error) + } + }, + deleteMerchantTables: function () { + LNbits.utils + .confirmDialog( + ` + Stalls, products and orders will be deleted also! + Are you sure you want to delete this merchant? + ` + ) + .onOk(async () => { + try { + await LNbits.api.request( + 'DELETE', + '/nostrmarket/api/v1/merchant/' + this.merchantId, + this.adminkey + ) + this.$emit('merchant-deleted', this.merchantId) + this.$q.notify({ + type: 'positive', + message: 'Merchant Deleted', + timeout: 5000 + }) + } catch (error) { + console.warn(error) + LNbits.utils.notifyApiError(error) + } + }) + }, + deleteMerchantFromNostr: function () { + LNbits.utils + .confirmDialog( + ` + Do you want to remove the merchant from Nostr? + ` + ) + .onOk(async () => { + try { + await LNbits.api.request( + 'DELETE', + `/nostrmarket/api/v1/merchant/${this.merchantId}/nostr`, + this.adminkey + ) + this.$q.notify({ + type: 'positive', + message: 'Merchant Deleted from Nostr', + timeout: 5000 + }) + } catch (error) { + console.warn(error) + LNbits.utils.notifyApiError(error) + } + }) + } + }, + created: async function () {} +}) diff --git a/static/components/merchant-details/merchant-details.js b/static/components/merchant-details/merchant-details.js deleted file mode 100644 index ed78226..0000000 --- a/static/components/merchant-details/merchant-details.js +++ /dev/null @@ -1,106 +0,0 @@ -async function merchantDetails(path) { - const template = await loadTemplateAsync(path) - Vue.component('merchant-details', { - name: 'merchant-details', - props: ['merchant-id', 'adminkey', 'inkey','showKeys'], - template, - - data: function () { - return { - } - }, - methods: { - toggleShowKeys: async function () { - this.$emit('toggle-show-keys') - }, - - republishMerchantData: async function () { - try { - await LNbits.api.request( - 'PUT', - `/nostrmarket/api/v1/merchant/${this.merchantId}/nostr`, - this.adminkey - ) - this.$q.notify({ - type: 'positive', - message: 'Merchant data republished to Nostr', - timeout: 5000 - }) - } catch (error) { - console.warn(error) - LNbits.utils.notifyApiError(error) - } - }, - requeryMerchantData: async function () { - try { - await LNbits.api.request( - 'GET', - `/nostrmarket/api/v1/merchant/${this.merchantId}/nostr`, - this.adminkey - ) - this.$q.notify({ - type: 'positive', - message: 'Merchant data refreshed from Nostr', - timeout: 5000 - }) - } catch (error) { - console.warn(error) - LNbits.utils.notifyApiError(error) - } - }, - deleteMerchantTables: function () { - LNbits.utils - .confirmDialog( - ` - Stalls, products and orders will be deleted also! - Are you sure you want to delete this merchant? - ` - ) - .onOk(async () => { - try { - await LNbits.api.request( - 'DELETE', - '/nostrmarket/api/v1/merchant/' + this.merchantId, - this.adminkey - ) - this.$emit('merchant-deleted', this.merchantId) - this.$q.notify({ - type: 'positive', - message: 'Merchant Deleted', - timeout: 5000 - }) - } catch (error) { - console.warn(error) - LNbits.utils.notifyApiError(error) - } - }) - }, - deleteMerchantFromNostr: function () { - LNbits.utils - .confirmDialog( - ` - Do you want to remove the merchant from Nostr? - ` - ) - .onOk(async () => { - try { - await LNbits.api.request( - 'DELETE', - `/nostrmarket/api/v1/merchant/${this.merchantId}/nostr`, - this.adminkey - ) - this.$q.notify({ - type: 'positive', - message: 'Merchant Deleted from Nostr', - timeout: 5000 - }) - } catch (error) { - console.warn(error) - LNbits.utils.notifyApiError(error) - } - }) - } - }, - created: async function () {} - }) -} diff --git a/static/components/order-list.js b/static/components/order-list.js new file mode 100644 index 0000000..f936fba --- /dev/null +++ b/static/components/order-list.js @@ -0,0 +1,406 @@ +window.app.component('order-list', { + name: 'order-list', + props: ['stall-id', 'customer-pubkey-filter', 'adminkey', 'inkey'], + template: '#order-list', + delimiters: ['${', '}'], + watch: { + customerPubkeyFilter: async function (n) { + this.search.publicKey = n + this.search.isPaid = {label: 'All', id: null} + this.search.isShipped = {label: 'All', id: null} + await this.getOrders() + } + }, + + data: function () { + return { + orders: [], + stalls: [], + selectedOrder: null, + shippingMessage: '', + showShipDialog: false, + filter: '', + search: { + publicKey: null, + isPaid: { + label: 'All', + id: null + }, + isShipped: { + label: 'All', + id: null + }, + restoring: false + }, + customers: [], + ternaryOptions: [ + { + label: 'All', + id: null + }, + { + label: 'Yes', + id: 'true' + }, + { + label: 'No', + id: 'false' + } + ], + zoneOptions: [], + ordersTable: { + columns: [ + { + name: '', + align: 'left', + label: '', + field: '' + }, + { + name: 'id', + align: 'left', + label: 'Order ID', + field: 'id' + }, + { + name: 'total', + align: 'left', + label: 'Total Sats', + field: 'total' + }, + { + name: 'fiat', + align: 'left', + label: 'Total Fiat', + field: 'fiat' + }, + { + name: 'paid', + align: 'left', + label: 'Paid', + field: 'paid' + }, + { + name: 'shipped', + align: 'left', + label: 'Shipped', + field: 'shipped' + }, + { + name: 'public_key', + align: 'left', + label: 'Customer', + field: 'pubkey' + }, + { + name: 'event_created_at', + align: 'left', + label: 'Created At', + field: 'event_created_at' + } + ], + pagination: { + rowsPerPage: 10 + } + } + } + }, + computed: { + customerOptions: function () { + const options = this.customers.map(c => ({ + label: this.buildCustomerLabel(c), + value: c.public_key + })) + options.unshift({label: 'All', value: null, id: null}) + return options + } + }, + methods: { + toShortId: function (value) { + return value.substring(0, 5) + '...' + value.substring(value.length - 5) + }, + formatDate: function (value) { + return Quasar.date.formatDate(new Date(value * 1000), 'YYYY-MM-DD HH:mm') + }, + satBtc(val, showUnit = true) { + return satOrBtc(val, showUnit, true) + }, + formatFiat(value, currency) { + return Math.trunc(value) + ' ' + currency + }, + shortLabel(value = '') { + if (value.length <= 44) return value + return value.substring(0, 20) + '...' + }, + productName: function (order, productId) { + product = order.extra.products.find(p => p.id === productId) + if (product) { + return product.name + } + return '' + }, + productPrice: function (order, productId) { + product = order.extra.products.find(p => p.id === productId) + if (product) { + return `${product.price} ${order.extra.currency}` + } + return '' + }, + orderTotal: function (order) { + const productCost = order.items.reduce((t, item) => { + product = order.extra.products.find(p => p.id === item.product_id) + return t + item.quantity * product.price + }, 0) + return productCost + order.extra.shipping_cost + }, + getOrders: async function () { + try { + const ordersPath = this.stallId + ? `stall/order/${this.stallId}` + : 'order' + + const query = [] + if (this.search.publicKey) { + query.push(`pubkey=${this.search.publicKey}`) + } + if (this.search.isPaid.id) { + query.push(`paid=${this.search.isPaid.id}`) + } + if (this.search.isShipped.id) { + query.push(`shipped=${this.search.isShipped.id}`) + } + const {data} = await LNbits.api.request( + 'GET', + `/nostrmarket/api/v1/${ordersPath}?${query.join('&')}`, + this.inkey + ) + this.orders = data.map(s => ({...s, expanded: false})) + } catch (error) { + LNbits.utils.notifyApiError(error) + } + }, + getOrder: async function (orderId) { + try { + const {data} = await LNbits.api.request( + 'GET', + `/nostrmarket/api/v1/order/${orderId}`, + this.inkey + ) + return {...data, expanded: false, isNew: true} + } catch (error) { + LNbits.utils.notifyApiError(error) + } + }, + restoreOrder: async function (eventId) { + console.log('### restoreOrder', eventId) + try { + this.search.restoring = true + const {data} = await LNbits.api.request( + 'PUT', + `/nostrmarket/api/v1/order/restore/${eventId}`, + this.adminkey + ) + await this.getOrders() + this.$q.notify({ + type: 'positive', + message: 'Order restored!' + }) + return data + } catch (error) { + LNbits.utils.notifyApiError(error) + } finally { + this.search.restoring = false + } + }, + restoreOrders: async function () { + try { + this.search.restoring = true + await LNbits.api.request( + 'PUT', + `/nostrmarket/api/v1/orders/restore`, + this.adminkey + ) + await this.getOrders() + this.$q.notify({ + type: 'positive', + message: 'Orders restored!' + }) + } catch (error) { + LNbits.utils.notifyApiError(error) + } finally { + this.search.restoring = false + } + }, + reissueOrderInvoice: async function (order) { + try { + const {data} = await LNbits.api.request( + 'PUT', + `/nostrmarket/api/v1/order/reissue`, + this.adminkey, + { + id: order.id, + shipping_id: order.shipping_id + } + ) + this.$q.notify({ + type: 'positive', + message: 'Order invoice reissued!' + }) + data.expanded = order.expanded + + const i = this.orders.map(o => o.id).indexOf(order.id) + if (i !== -1) { + this.orders[i] = {...this.orders[i], ...data} + } + } catch (error) { + LNbits.utils.notifyApiError(error) + } + }, + updateOrderShipped: async function () { + this.selectedOrder.shipped = !this.selectedOrder.shipped + try { + await LNbits.api.request( + 'PATCH', + `/nostrmarket/api/v1/order/${this.selectedOrder.id}`, + this.adminkey, + { + id: this.selectedOrder.id, + message: this.shippingMessage, + shipped: this.selectedOrder.shipped + } + ) + this.$q.notify({ + type: 'positive', + message: 'Order updated!' + }) + } catch (error) { + LNbits.utils.notifyApiError(error) + } + this.showShipDialog = false + }, + addOrder: async function (data) { + if ( + !this.search.publicKey || + this.search.publicKey === data.customerPubkey + ) { + const orderData = JSON.parse(data.dm.message) + const i = this.orders.map(o => o.id).indexOf(orderData.id) + if (i === -1) { + const order = await this.getOrder(orderData.id) + this.orders.unshift(order) + } + } + }, + orderSelected: async function (orderId, eventId) { + const order = await this.getOrder(orderId) + if (!order) { + LNbits.utils + .confirmDialog( + 'Order could not be found. Do you want to restore it from this direct message?' + ) + .onOk(async () => { + const restoredOrder = await this.restoreOrder(eventId) + console.log('### restoredOrder', restoredOrder) + if (restoredOrder) { + restoredOrder.expanded = true + restoredOrder.isNew = false + this.orders = [restoredOrder] + } + }) + return + } + order.expanded = true + order.isNew = false + this.orders = [order] + }, + getZones: async function () { + try { + const {data} = await LNbits.api.request( + 'GET', + '/nostrmarket/api/v1/zone', + this.inkey + ) + return data.map(z => ({ + id: z.id, + value: z.id, + label: z.name + ? `${z.name} (${z.countries.join(', ')})` + : z.countries.join(', ') + })) + } catch (error) { + LNbits.utils.notifyApiError(error) + } + return [] + }, + getStalls: async function (pending = false) { + try { + const {data} = await LNbits.api.request( + 'GET', + `/nostrmarket/api/v1/stall?pending=${pending}`, + this.inkey + ) + return data.map(s => ({...s, expanded: false})) + } catch (error) { + LNbits.utils.notifyApiError(error) + } + return [] + }, + getStallZones: function (stallId) { + const stall = this.stalls.find(s => s.id === stallId) + if (!stall) return [] + + return this.zoneOptions.filter(z => + stall.shipping_zones.find(s => s.id === z.id) + ) + }, + showShipOrderDialog: function (order) { + this.selectedOrder = order + this.shippingMessage = order.shipped + ? 'The order has been shipped!' + : 'The order has NOT yet been shipped!' + + // do not change the status yet + this.selectedOrder.shipped = !order.shipped + this.showShipDialog = true + }, + customerSelected: function (customerPubkey) { + this.$emit('customer-selected', customerPubkey) + }, + getCustomers: async function () { + try { + const {data} = await LNbits.api.request( + 'GET', + '/nostrmarket/api/v1/customer', + this.inkey + ) + this.customers = data + } catch (error) { + LNbits.utils.notifyApiError(error) + } + }, + buildCustomerLabel: function (c) { + let label = `${c.profile.name || 'unknown'} ${c.profile.about || ''}` + if (c.unread_messages) { + label += `[new: ${c.unread_messages}]` + } + label += ` (${c.public_key.slice(0, 16)}...${c.public_key.slice( + c.public_key.length - 16 + )}` + return label + }, + orderPaid: function (orderId) { + const order = this.orders.find(o => o.id === orderId) + if (order) { + order.paid = true + } + } + }, + created: async function () { + if (this.stallId) { + await this.getOrders() + } + await this.getCustomers() + this.zoneOptions = await this.getZones() + this.stalls = await this.getStalls() + } +}) diff --git a/static/components/order-list/order-list.html b/static/components/order-list/order-list.html deleted file mode 100644 index 44feb44..0000000 --- a/static/components/order-list/order-list.html +++ /dev/null @@ -1,212 +0,0 @@ -
-
-
- - -
-
- - -
-
- - -
-
- - - - - - Restore Orders - Restore previous orders from Nostr - - - -
-
-
-
- - - -
-
- - - - - -
- - - Cancel -
-
-
-
-
diff --git a/static/components/order-list/order-list.js b/static/components/order-list/order-list.js deleted file mode 100644 index 2dc721a..0000000 --- a/static/components/order-list/order-list.js +++ /dev/null @@ -1,409 +0,0 @@ -async function orderList(path) { - const template = await loadTemplateAsync(path) - Vue.component('order-list', { - name: 'order-list', - props: ['stall-id', 'customer-pubkey-filter', 'adminkey', 'inkey'], - template, - - watch: { - customerPubkeyFilter: async function (n) { - this.search.publicKey = n - this.search.isPaid = { label: 'All', id: null } - this.search.isShipped = { label: 'All', id: null } - await this.getOrders() - } - }, - - data: function () { - return { - orders: [], - stalls: [], - selectedOrder: null, - shippingMessage: '', - showShipDialog: false, - filter: '', - search: { - publicKey: null, - isPaid: { - label: 'All', - id: null - }, - isShipped: { - label: 'All', - id: null - }, - restoring: false - }, - customers: [], - ternaryOptions: [ - { - label: 'All', - id: null - }, - { - label: 'Yes', - id: 'true' - }, - { - label: 'No', - id: 'false' - } - ], - zoneOptions: [], - ordersTable: { - columns: [ - { - name: '', - align: 'left', - label: '', - field: '' - }, - { - name: 'id', - align: 'left', - label: 'Order ID', - field: 'id' - }, - { - name: 'total', - align: 'left', - label: 'Total Sats', - field: 'total' - }, - { - name: 'fiat', - align: 'left', - label: 'Total Fiat', - field: 'fiat' - }, - { - name: 'paid', - align: 'left', - label: 'Paid', - field: 'paid' - }, - { - name: 'shipped', - align: 'left', - label: 'Shipped', - field: 'shipped' - }, - { - name: 'public_key', - align: 'left', - label: 'Customer', - field: 'pubkey' - }, - { - name: 'event_created_at', - align: 'left', - label: 'Created At', - field: 'event_created_at' - } - ], - pagination: { - rowsPerPage: 10 - } - } - } - }, - computed: { - customerOptions: function () { - const options = this.customers.map(c => ({ label: this.buildCustomerLabel(c), value: c.public_key })) - options.unshift({ label: 'All', value: null, id: null }) - return options - } - }, - methods: { - toShortId: function (value) { - return value.substring(0, 5) + '...' + value.substring(value.length - 5) - }, - formatDate: function (value) { - return Quasar.utils.date.formatDate( - new Date(value * 1000), - 'YYYY-MM-DD HH:mm' - ) - }, - satBtc(val, showUnit = true) { - return satOrBtc(val, showUnit, true) - }, - formatFiat(value, currency) { - return Math.trunc(value) + ' ' + currency - }, - shortLabel(value = ''){ - if (value.length <= 44) return value - return value.substring(0, 20) + '...' - }, - productName: function (order, productId) { - product = order.extra.products.find(p => p.id === productId) - if (product) { - return product.name - } - return '' - }, - productPrice: function (order, productId) { - product = order.extra.products.find(p => p.id === productId) - if (product) { - return `${product.price} ${order.extra.currency}` - } - return '' - }, - orderTotal: function (order) { - const productCost = order.items.reduce((t, item) => { - product = order.extra.products.find(p => p.id === item.product_id) - return t + item.quantity * product.price - }, 0) - return productCost + order.extra.shipping_cost - }, - getOrders: async function () { - try { - const ordersPath = this.stallId - ? `stall/order/${this.stallId}` - : 'order' - - const query = [] - if (this.search.publicKey) { - query.push(`pubkey=${this.search.publicKey}`) - } - if (this.search.isPaid.id) { - query.push(`paid=${this.search.isPaid.id}`) - } - if (this.search.isShipped.id) { - query.push(`shipped=${this.search.isShipped.id}`) - } - const { data } = await LNbits.api.request( - 'GET', - `/nostrmarket/api/v1/${ordersPath}?${query.join('&')}`, - this.inkey - ) - this.orders = data.map(s => ({ ...s, expanded: false })) - } catch (error) { - LNbits.utils.notifyApiError(error) - } - }, - getOrder: async function (orderId) { - try { - const { data } = await LNbits.api.request( - 'GET', - `/nostrmarket/api/v1/order/${orderId}`, - this.inkey - ) - return { ...data, expanded: false, isNew: true } - } catch (error) { - LNbits.utils.notifyApiError(error) - } - }, - restoreOrder: async function (eventId) { - console.log('### restoreOrder', eventId) - try { - this.search.restoring = true - const {data} = await LNbits.api.request( - 'PUT', - `/nostrmarket/api/v1/order/restore/${eventId}`, - this.adminkey - ) - await this.getOrders() - this.$q.notify({ - type: 'positive', - message: 'Order restored!' - }) - return data - } catch (error) { - LNbits.utils.notifyApiError(error) - } finally { - this.search.restoring = false - } - }, - restoreOrders: async function () { - try { - this.search.restoring = true - await LNbits.api.request( - 'PUT', - `/nostrmarket/api/v1/orders/restore`, - this.adminkey - ) - await this.getOrders() - this.$q.notify({ - type: 'positive', - message: 'Orders restored!' - }) - } catch (error) { - LNbits.utils.notifyApiError(error) - } finally { - this.search.restoring = false - } - }, - reissueOrderInvoice: async function (order) { - try { - const { data } = await LNbits.api.request( - 'PUT', - `/nostrmarket/api/v1/order/reissue`, - this.adminkey, - { - id: order.id, - shipping_id: order.shipping_id - } - ) - this.$q.notify({ - type: 'positive', - message: 'Order invoice reissued!' - }) - data.expanded = order.expanded - - const i = this.orders.map(o => o.id).indexOf(order.id) - if (i !== -1) { - this.orders[i] = { ...this.orders[i], ...data } - } - } catch (error) { - LNbits.utils.notifyApiError(error) - } - }, - updateOrderShipped: async function () { - this.selectedOrder.shipped = !this.selectedOrder.shipped - try { - await LNbits.api.request( - 'PATCH', - `/nostrmarket/api/v1/order/${this.selectedOrder.id}`, - this.adminkey, - { - id: this.selectedOrder.id, - message: this.shippingMessage, - shipped: this.selectedOrder.shipped - } - ) - this.$q.notify({ - type: 'positive', - message: 'Order updated!' - }) - } catch (error) { - LNbits.utils.notifyApiError(error) - } - this.showShipDialog = false - }, - addOrder: async function (data) { - if ( - !this.search.publicKey || - this.search.publicKey === data.customerPubkey - ) { - const orderData = JSON.parse(data.dm.message) - const i = this.orders.map(o => o.id).indexOf(orderData.id) - if (i === -1) { - const order = await this.getOrder(orderData.id) - this.orders.unshift(order) - } - - } - }, - orderSelected: async function (orderId, eventId) { - const order = await this.getOrder(orderId) - if (!order) { - LNbits.utils - .confirmDialog( - "Order could not be found. Do you want to restore it from this direct message?" - ) - .onOk(async () => { - const restoredOrder = await this.restoreOrder(eventId) - console.log('### restoredOrder', restoredOrder) - if (restoredOrder) { - restoredOrder.expanded = true - restoredOrder.isNew = false - this.orders = [restoredOrder] - } - - }) - return - } - order.expanded = true - order.isNew = false - this.orders = [order] - }, - getZones: async function () { - try { - const { data } = await LNbits.api.request( - 'GET', - '/nostrmarket/api/v1/zone', - this.inkey - ) - return data.map(z => ({ - id: z.id, - value: z.id, - label: z.name - ? `${z.name} (${z.countries.join(', ')})` - : z.countries.join(', ') - })) - } catch (error) { - LNbits.utils.notifyApiError(error) - } - return [] - }, - getStalls: async function (pending = false) { - try { - const { data } = await LNbits.api.request( - 'GET', - `/nostrmarket/api/v1/stall?pending=${pending}`, - this.inkey - ) - return data.map(s => ({ ...s, expanded: false })) - } catch (error) { - LNbits.utils.notifyApiError(error) - } - return [] - }, - getStallZones: function (stallId) { - const stall = this.stalls.find(s => s.id === stallId) - if (!stall) return [] - - return this.zoneOptions.filter(z => stall.shipping_zones.find(s => s.id === z.id)) - }, - showShipOrderDialog: function (order) { - this.selectedOrder = order - this.shippingMessage = order.shipped - ? 'The order has been shipped!' - : 'The order has NOT yet been shipped!' - - // do not change the status yet - this.selectedOrder.shipped = !order.shipped - this.showShipDialog = true - }, - customerSelected: function (customerPubkey) { - this.$emit('customer-selected', customerPubkey) - }, - getCustomers: async function () { - try { - const { data } = await LNbits.api.request( - 'GET', - '/nostrmarket/api/v1/customer', - this.inkey - ) - this.customers = data - } catch (error) { - LNbits.utils.notifyApiError(error) - } - }, - buildCustomerLabel: function (c) { - let label = `${c.profile.name || 'unknown'} ${c.profile.about || ''}` - if (c.unread_messages) { - label += `[new: ${c.unread_messages}]` - } - label += ` (${c.public_key.slice(0, 16)}...${c.public_key.slice( - c.public_key.length - 16 - )}` - return label - }, - orderPaid: function (orderId) { - const order = this.orders.find(o => o.id === orderId) - if (order) { - order.paid = true - } - } - }, - created: async function () { - if (this.stallId) { - await this.getOrders() - } - await this.getCustomers() - this.zoneOptions = await this.getZones() - this.stalls = await this.getStalls() - } - }) -} diff --git a/static/components/shipping-zones.js b/static/components/shipping-zones.js new file mode 100644 index 0000000..742021a --- /dev/null +++ b/static/components/shipping-zones.js @@ -0,0 +1,183 @@ +window.app.component('shipping-zones', { + name: 'shipping-zones', + props: ['adminkey', 'inkey'], + template: '#shipping-zones', + delimiters: ['${', '}'], + data: function () { + return { + zones: [], + zoneDialog: { + showDialog: false, + data: { + id: null, + name: '', + countries: [], + cost: 0, + currency: 'sat' + } + }, + currencies: [], + shippingZoneOptions: [ + 'Free (digital)', + 'Flat rate', + 'Worldwide', + 'Europe', + 'Australia', + 'Austria', + 'Belgium', + 'Brazil', + 'Canada', + 'Denmark', + 'Finland', + 'France', + 'Germany', + 'Greece', + 'Hong Kong', + 'Hungary', + 'Ireland', + 'Indonesia', + 'Israel', + 'Italy', + 'Japan', + 'Kazakhstan', + 'Korea', + 'Luxembourg', + 'Malaysia', + 'Mexico', + 'Netherlands', + 'New Zealand', + 'Norway', + 'Poland', + 'Portugal', + 'Romania', + 'Russia', + 'Saudi Arabia', + 'Singapore', + 'Spain', + 'Sweden', + 'Switzerland', + 'Thailand', + 'Turkey', + 'Ukraine', + 'United Kingdom**', + 'United States***', + 'Vietnam', + 'China' + ] + } + }, + methods: { + openZoneDialog: function (data) { + data = data || { + id: null, + name: '', + countries: [], + cost: 0, + currency: 'sat' + } + this.zoneDialog.data = data + + this.zoneDialog.showDialog = true + }, + createZone: async function () { + try { + const {data} = await LNbits.api.request( + 'POST', + '/nostrmarket/api/v1/zone', + this.adminkey, + {} + ) + this.zones = data + } catch (error) { + LNbits.utils.notifyApiError(error) + } + }, + getZones: async function () { + try { + const {data} = await LNbits.api.request( + 'GET', + '/nostrmarket/api/v1/zone', + this.inkey + ) + this.zones = data + } catch (error) { + LNbits.utils.notifyApiError(error) + } + }, + sendZoneFormData: async function () { + this.zoneDialog.showDialog = false + if (this.zoneDialog.data.id) { + await this.updateShippingZone(this.zoneDialog.data) + } else { + await this.createShippingZone(this.zoneDialog.data) + } + await this.getZones() + }, + createShippingZone: async function (newZone) { + try { + await LNbits.api.request( + 'POST', + '/nostrmarket/api/v1/zone', + this.adminkey, + newZone + ) + this.$q.notify({ + type: 'positive', + message: 'Zone created!' + }) + } catch (error) { + LNbits.utils.notifyApiError(error) + } + }, + updateShippingZone: async function (updatedZone) { + try { + await LNbits.api.request( + 'PATCH', + `/nostrmarket/api/v1/zone/${updatedZone.id}`, + this.adminkey, + updatedZone + ) + this.$q.notify({ + type: 'positive', + message: 'Zone updated!' + }) + } catch (error) { + LNbits.utils.notifyApiError(error) + } + }, + deleteShippingZone: async function () { + try { + await LNbits.api.request( + 'DELETE', + `/nostrmarket/api/v1/zone/${this.zoneDialog.data.id}`, + this.adminkey + ) + this.$q.notify({ + type: 'positive', + message: 'Zone deleted!' + }) + await this.getZones() + this.zoneDialog.showDialog = false + } catch (error) { + LNbits.utils.notifyApiError(error) + } + }, + async getCurrencies() { + try { + const {data} = await LNbits.api.request( + 'GET', + '/nostrmarket/api/v1/currencies', + this.inkey + ) + + this.currencies = ['sat', ...data] + } catch (error) { + LNbits.utils.notifyApiError(error) + } + } + }, + created: async function () { + await this.getZones() + await this.getCurrencies() + } +}) diff --git a/static/components/shipping-zones/shipping-zones.js b/static/components/shipping-zones/shipping-zones.js deleted file mode 100644 index 173d713..0000000 --- a/static/components/shipping-zones/shipping-zones.js +++ /dev/null @@ -1,186 +0,0 @@ -async function shippingZones(path) { - const template = await loadTemplateAsync(path) - Vue.component('shipping-zones', { - name: 'shipping-zones', - props: ['adminkey', 'inkey'], - template, - - data: function () { - return { - zones: [], - zoneDialog: { - showDialog: false, - data: { - id: null, - name: '', - countries: [], - cost: 0, - currency: 'sat' - } - }, - currencies: [], - shippingZoneOptions: [ - 'Free (digital)', - 'Flat rate', - 'Worldwide', - 'Europe', - 'Australia', - 'Austria', - 'Belgium', - 'Brazil', - 'Canada', - 'Denmark', - 'Finland', - 'France', - 'Germany', - 'Greece', - 'Hong Kong', - 'Hungary', - 'Ireland', - 'Indonesia', - 'Israel', - 'Italy', - 'Japan', - 'Kazakhstan', - 'Korea', - 'Luxembourg', - 'Malaysia', - 'Mexico', - 'Netherlands', - 'New Zealand', - 'Norway', - 'Poland', - 'Portugal', - 'Romania', - 'Russia', - 'Saudi Arabia', - 'Singapore', - 'Spain', - 'Sweden', - 'Switzerland', - 'Thailand', - 'Turkey', - 'Ukraine', - 'United Kingdom**', - 'United States***', - 'Vietnam', - 'China' - ] - } - }, - methods: { - openZoneDialog: function (data) { - data = data || { - id: null, - name: '', - countries: [], - cost: 0, - currency: 'sat' - } - this.zoneDialog.data = data - - this.zoneDialog.showDialog = true - }, - createZone: async function () { - try { - const {data} = await LNbits.api.request( - 'POST', - '/nostrmarket/api/v1/zone', - this.adminkey, - {} - ) - this.zones = data - } catch (error) { - LNbits.utils.notifyApiError(error) - } - }, - getZones: async function () { - try { - const {data} = await LNbits.api.request( - 'GET', - '/nostrmarket/api/v1/zone', - this.inkey - ) - this.zones = data - } catch (error) { - LNbits.utils.notifyApiError(error) - } - }, - sendZoneFormData: async function () { - this.zoneDialog.showDialog = false - if (this.zoneDialog.data.id) { - await this.updateShippingZone(this.zoneDialog.data) - } else { - await this.createShippingZone(this.zoneDialog.data) - } - await this.getZones() - }, - createShippingZone: async function (newZone) { - try { - await LNbits.api.request( - 'POST', - '/nostrmarket/api/v1/zone', - this.adminkey, - newZone - ) - this.$q.notify({ - type: 'positive', - message: 'Zone created!' - }) - } catch (error) { - LNbits.utils.notifyApiError(error) - } - }, - updateShippingZone: async function (updatedZone) { - try { - await LNbits.api.request( - 'PATCH', - `/nostrmarket/api/v1/zone/${updatedZone.id}`, - this.adminkey, - updatedZone - ) - this.$q.notify({ - type: 'positive', - message: 'Zone updated!' - }) - } catch (error) { - LNbits.utils.notifyApiError(error) - } - }, - deleteShippingZone: async function () { - try { - await LNbits.api.request( - 'DELETE', - `/nostrmarket/api/v1/zone/${this.zoneDialog.data.id}`, - this.adminkey - ) - this.$q.notify({ - type: 'positive', - message: 'Zone deleted!' - }) - await this.getZones() - this.zoneDialog.showDialog = false - } catch (error) { - LNbits.utils.notifyApiError(error) - } - }, - async getCurrencies() { - try { - const {data} = await LNbits.api.request( - 'GET', - '/nostrmarket/api/v1/currencies', - this.inkey - ) - - this.currencies = ['sat', ...data] - } catch (error) { - LNbits.utils.notifyApiError(error) - } - } - }, - created: async function () { - await this.getZones() - await this.getCurrencies() - } - }) -} diff --git a/static/components/stall-details.js b/static/components/stall-details.js new file mode 100644 index 0000000..be0633b --- /dev/null +++ b/static/components/stall-details.js @@ -0,0 +1,338 @@ +window.app.component('stall-details', { + name: 'stall-details', + template: '#stall-details', + delimiters: ['${', '}'], + props: [ + 'stall-id', + 'adminkey', + 'inkey', + 'wallet-options', + 'zone-options', + 'currencies' + ], + data: function () { + return { + tab: 'products', + stall: null, + products: [], + pendingProducts: [], + productDialog: { + showDialog: false, + showRestore: false, + url: true, + data: null + }, + productsFilter: '', + productsTable: { + columns: [ + { + name: 'delete', + align: 'left', + label: '', + field: '' + }, + { + name: 'edit', + align: 'left', + label: '', + field: '' + }, + { + name: 'activate', + align: 'left', + label: '', + field: '' + }, + + { + name: 'id', + align: 'left', + label: 'ID', + field: 'id' + }, + { + name: 'name', + align: 'left', + label: 'Name', + field: 'name' + }, + { + name: 'price', + align: 'left', + label: 'Price', + field: 'price' + }, + { + name: 'quantity', + align: 'left', + label: 'Quantity', + field: 'quantity' + } + ], + pagination: { + rowsPerPage: 10 + } + } + } + }, + computed: { + filteredZoneOptions: function () { + if (!this.stall) return [] + return this.zoneOptions.filter(z => z.currency === this.stall.currency) + } + }, + methods: { + mapStall: function (stall) { + stall.shipping_zones.forEach( + z => + (z.label = z.name + ? `${z.name} (${z.countries.join(', ')})` + : z.countries.join(', ')) + ) + return stall + }, + newEmtpyProductData: function () { + return { + id: null, + name: '', + categories: [], + images: [], + image: null, + price: 0, + + quantity: 0, + config: { + description: '', + use_autoreply: false, + autoreply_message: '', + shipping: (this.stall.shipping_zones || []).map(z => ({ + id: z.id, + name: z.name, + cost: 0 + })) + } + } + }, + getStall: async function () { + try { + const {data} = await LNbits.api.request( + 'GET', + '/nostrmarket/api/v1/stall/' + this.stallId, + this.inkey + ) + this.stall = this.mapStall(data) + } catch (error) { + LNbits.utils.notifyApiError(error) + } + }, + updateStall: async function () { + try { + const {data} = await LNbits.api.request( + 'PUT', + '/nostrmarket/api/v1/stall/' + this.stallId, + this.adminkey, + this.stall + ) + this.stall = this.mapStall(data) + this.$emit('stall-updated', this.stall) + this.$q.notify({ + type: 'positive', + message: 'Stall Updated', + timeout: 5000 + }) + } catch (error) { + console.warn(error) + LNbits.utils.notifyApiError(error) + } + }, + deleteStall: function () { + LNbits.utils + .confirmDialog( + ` + Products and orders will be deleted also! + Are you sure you want to delete this stall? + ` + ) + .onOk(async () => { + try { + await LNbits.api.request( + 'DELETE', + '/nostrmarket/api/v1/stall/' + this.stallId, + this.adminkey + ) + this.$emit('stall-deleted', this.stallId) + this.$q.notify({ + type: 'positive', + message: 'Stall Deleted', + timeout: 5000 + }) + } catch (error) { + console.warn(error) + LNbits.utils.notifyApiError(error) + } + }) + }, + addProductImage: function () { + if (!isValidImageUrl(this.productDialog.data.image)) { + this.$q.notify({ + type: 'warning', + message: 'Not a valid image URL', + timeout: 5000 + }) + return + } + this.productDialog.data.images.push(this.productDialog.data.image) + this.productDialog.data.image = null + }, + removeProductImage: function (imageUrl) { + const index = this.productDialog.data.images.indexOf(imageUrl) + if (index !== -1) { + this.productDialog.data.images.splice(index, 1) + } + }, + getProducts: async function (pending = false) { + try { + const {data} = await LNbits.api.request( + 'GET', + `/nostrmarket/api/v1/stall/product/${this.stall.id}?pending=${pending}`, + this.inkey + ) + return data + } catch (error) { + LNbits.utils.notifyApiError(error) + } + }, + sendProductFormData: function () { + const data = { + stall_id: this.stall.id, + id: this.productDialog.data.id, + name: this.productDialog.data.name, + + images: this.productDialog.data.images, + price: this.productDialog.data.price, + quantity: this.productDialog.data.quantity, + categories: this.productDialog.data.categories, + config: this.productDialog.data.config + } + this.productDialog.showDialog = false + if (this.productDialog.data.id) { + data.pending = false + this.updateProduct(data) + } else { + this.createProduct(data) + } + }, + updateProduct: async function (product) { + try { + const {data} = await LNbits.api.request( + 'PATCH', + '/nostrmarket/api/v1/product/' + product.id, + this.adminkey, + product + ) + const index = this.products.findIndex(r => r.id === product.id) + if (index !== -1) { + this.products.splice(index, 1, data) + } else { + this.products.unshift(data) + } + this.$q.notify({ + type: 'positive', + message: 'Product Updated', + timeout: 5000 + }) + } catch (error) { + console.warn(error) + LNbits.utils.notifyApiError(error) + } + }, + createProduct: async function (payload) { + try { + const {data} = await LNbits.api.request( + 'POST', + '/nostrmarket/api/v1/product', + this.adminkey, + payload + ) + this.products.unshift(data) + this.$q.notify({ + type: 'positive', + message: 'Product Created', + timeout: 5000 + }) + } catch (error) { + console.warn(error) + LNbits.utils.notifyApiError(error) + } + }, + editProduct: async function (product) { + const emptyShipping = this.newEmtpyProductData().config.shipping + this.productDialog.data = {...product} + this.productDialog.data.config.shipping = emptyShipping.map( + shippingZone => { + const existingShippingCost = (product.config.shipping || []).find( + ps => ps.id === shippingZone.id + ) + shippingZone.cost = existingShippingCost?.cost || 0 + return shippingZone + } + ) + + this.productDialog.showDialog = true + }, + deleteProduct: async function (productId) { + LNbits.utils + .confirmDialog('Are you sure you want to delete this product?') + .onOk(async () => { + try { + await LNbits.api.request( + 'DELETE', + '/nostrmarket/api/v1/product/' + productId, + this.adminkey + ) + this.products = _.reject(this.products, function (obj) { + return obj.id === productId + }) + this.$q.notify({ + type: 'positive', + message: 'Product deleted', + timeout: 5000 + }) + } catch (error) { + console.warn(error) + LNbits.utils.notifyApiError(error) + } + }) + }, + showNewProductDialog: async function (data) { + this.productDialog.data = data || this.newEmtpyProductData() + this.productDialog.showDialog = true + }, + openSelectPendingProductDialog: async function () { + this.productDialog.showRestore = true + this.pendingProducts = await this.getProducts(true) + }, + openRestoreProductDialog: async function (pendingProduct) { + pendingProduct.pending = true + await this.showNewProductDialog(pendingProduct) + }, + restoreAllPendingProducts: async function () { + for (const p of this.pendingProducts) { + p.pending = false + await this.updateProduct(p) + } + }, + customerSelectedForOrder: function (customerPubkey) { + this.$emit('customer-selected-for-order', customerPubkey) + }, + shortLabel(value = '') { + if (value.length <= 44) return value + return value.substring(0, 40) + '...' + } + }, + created: async function () { + await this.getStall() + this.products = await this.getProducts() + this.productDialog.data = this.newEmtpyProductData() + } +}) diff --git a/static/components/stall-details/stall-details.html b/static/components/stall-details/stall-details.html deleted file mode 100644 index bb07526..0000000 --- a/static/components/stall-details/stall-details.html +++ /dev/null @@ -1,255 +0,0 @@ -
- - - - - - - -
-
-
ID:
-
- -
-
-
-
-
Name:
-
- -
-
-
-
-
Description:
-
- -
-
-
-
-
Wallet:
-
- - -
-
-
-
-
Currency:
-
- -
-
-
-
-
Shipping Zones:
-
- -
-
-
-
-
-
- Update Stall -
-
- Delete Stall -
-
-
- -
-
-
- - - - - New Product - Create a new product - - - - - Restore Product - Restore existing product from Nostr - - - - -
-
-
-
- -
-
- - - -
-
-
-
- -
- -
-
-
- - - - - - - -
-
- -
-
- -
-
- - - -
- -
-
- - -
- - - - - - -
-
- - - -
-
- -
-
- - -
-
-
- - - -
-
- -
-
- -
-
- - -
-
-
-
-
- - -
- - - Create Product - - Cancel -
- - -
-
-
- - -
- - - - - - - - Restore - - - - - -
-
- There are no products to be restored. -
-
- Restore All - Close -
-
-
-
\ No newline at end of file diff --git a/static/components/stall-details/stall-details.js b/static/components/stall-details/stall-details.js deleted file mode 100644 index 8a6cf8c..0000000 --- a/static/components/stall-details/stall-details.js +++ /dev/null @@ -1,334 +0,0 @@ -async function stallDetails(path) { - const template = await loadTemplateAsync(path) - - Vue.component('stall-details', { - name: 'stall-details', - template, - - props: [ - 'stall-id', - 'adminkey', - 'inkey', - 'wallet-options', - 'zone-options', - 'currencies' - ], - data: function () { - return { - tab: 'products', - stall: null, - products: [], - pendingProducts: [], - productDialog: { - showDialog: false, - showRestore: false, - url: true, - data: null - }, - productsFilter: '', - productsTable: { - columns: [ - { - name: 'delete', - align: 'left', - label: '', - field: '' - }, - { - name: 'edit', - align: 'left', - label: '', - field: '' - }, - { - name: 'activate', - align: 'left', - label: '', - field: '' - }, - - { - name: 'id', - align: 'left', - label: 'ID', - field: 'id' - }, - { - name: 'name', - align: 'left', - label: 'Name', - field: 'name' - }, - { - name: 'price', - align: 'left', - label: 'Price', - field: 'price' - }, - { - name: 'quantity', - align: 'left', - label: 'Quantity', - field: 'quantity' - } - ], - pagination: { - rowsPerPage: 10 - } - } - } - }, - computed: { - filteredZoneOptions: function () { - if (!this.stall) return [] - return this.zoneOptions.filter(z => z.currency === this.stall.currency) - } - }, - methods: { - mapStall: function (stall) { - stall.shipping_zones.forEach( - z => - (z.label = z.name - ? `${z.name} (${z.countries.join(', ')})` - : z.countries.join(', ')) - ) - return stall - }, - newEmtpyProductData: function() { - return { - id: null, - name: '', - categories: [], - images: [], - image: null, - price: 0, - - quantity: 0, - config: { - description: '', - use_autoreply: false, - autoreply_message: '', - shipping: (this.stall.shipping_zones || []).map(z => ({id: z.id, name: z.name, cost: 0})) - } - } - }, - getStall: async function () { - try { - const { data } = await LNbits.api.request( - 'GET', - '/nostrmarket/api/v1/stall/' + this.stallId, - this.inkey - ) - this.stall = this.mapStall(data) - } catch (error) { - LNbits.utils.notifyApiError(error) - } - }, - updateStall: async function () { - try { - const { data } = await LNbits.api.request( - 'PUT', - '/nostrmarket/api/v1/stall/' + this.stallId, - this.adminkey, - this.stall - ) - this.stall = this.mapStall(data) - this.$emit('stall-updated', this.stall) - this.$q.notify({ - type: 'positive', - message: 'Stall Updated', - timeout: 5000 - }) - } catch (error) { - console.warn(error) - LNbits.utils.notifyApiError(error) - } - }, - deleteStall: function () { - LNbits.utils - .confirmDialog( - ` - Products and orders will be deleted also! - Are you sure you want to delete this stall? - ` - ) - .onOk(async () => { - try { - await LNbits.api.request( - 'DELETE', - '/nostrmarket/api/v1/stall/' + this.stallId, - this.adminkey - ) - this.$emit('stall-deleted', this.stallId) - this.$q.notify({ - type: 'positive', - message: 'Stall Deleted', - timeout: 5000 - }) - } catch (error) { - console.warn(error) - LNbits.utils.notifyApiError(error) - } - }) - }, - addProductImage: function () { - if (!isValidImageUrl(this.productDialog.data.image)) { - this.$q.notify({ - type: 'warning', - message: 'Not a valid image URL', - timeout: 5000 - }) - return - } - this.productDialog.data.images.push(this.productDialog.data.image) - this.productDialog.data.image = null - }, - removeProductImage: function (imageUrl) { - const index = this.productDialog.data.images.indexOf(imageUrl) - if (index !== -1) { - this.productDialog.data.images.splice(index, 1) - } - }, - getProducts: async function (pending = false) { - try { - const { data } = await LNbits.api.request( - 'GET', - `/nostrmarket/api/v1/stall/product/${this.stall.id}?pending=${pending}`, - this.inkey - ) - return data - } catch (error) { - LNbits.utils.notifyApiError(error) - } - }, - sendProductFormData: function () { - const data = { - stall_id: this.stall.id, - id: this.productDialog.data.id, - name: this.productDialog.data.name, - - images: this.productDialog.data.images, - price: this.productDialog.data.price, - quantity: this.productDialog.data.quantity, - categories: this.productDialog.data.categories, - config: this.productDialog.data.config - } - this.productDialog.showDialog = false - if (this.productDialog.data.id) { - data.pending = false - this.updateProduct(data) - } else { - this.createProduct(data) - } - }, - updateProduct: async function (product) { - try { - const { data } = await LNbits.api.request( - 'PATCH', - '/nostrmarket/api/v1/product/' + product.id, - this.adminkey, - product - ) - const index = this.products.findIndex(r => r.id === product.id) - if (index !== -1) { - this.products.splice(index, 1, data) - } else { - this.products.unshift(data) - } - this.$q.notify({ - type: 'positive', - message: 'Product Updated', - timeout: 5000 - }) - } catch (error) { - console.warn(error) - LNbits.utils.notifyApiError(error) - } - }, - createProduct: async function (payload) { - try { - const { data } = await LNbits.api.request( - 'POST', - '/nostrmarket/api/v1/product', - this.adminkey, - payload - ) - this.products.unshift(data) - this.$q.notify({ - type: 'positive', - message: 'Product Created', - timeout: 5000 - }) - } catch (error) { - console.warn(error) - LNbits.utils.notifyApiError(error) - } - }, - editProduct: async function (product) { - const emptyShipping = this.newEmtpyProductData().config.shipping - this.productDialog.data = { ...product } - this.productDialog.data.config.shipping = emptyShipping.map(shippingZone => { - const existingShippingCost = (product.config.shipping || []).find(ps => ps.id === shippingZone.id) - shippingZone.cost = existingShippingCost?.cost || 0 - return shippingZone - }) - - this.productDialog.showDialog = true - }, - deleteProduct: async function (productId) { - LNbits.utils - .confirmDialog('Are you sure you want to delete this product?') - .onOk(async () => { - try { - await LNbits.api.request( - 'DELETE', - '/nostrmarket/api/v1/product/' + productId, - this.adminkey - ) - this.products = _.reject(this.products, function (obj) { - return obj.id === productId - }) - this.$q.notify({ - type: 'positive', - message: 'Product deleted', - timeout: 5000 - }) - } catch (error) { - console.warn(error) - LNbits.utils.notifyApiError(error) - } - }) - }, - showNewProductDialog: async function (data) { - this.productDialog.data = data || this.newEmtpyProductData() - this.productDialog.showDialog = true - }, - openSelectPendingProductDialog: async function () { - this.productDialog.showRestore = true - this.pendingProducts = await this.getProducts(true) - }, - openRestoreProductDialog: async function (pendingProduct) { - pendingProduct.pending = true - await this.showNewProductDialog(pendingProduct) - }, - restoreAllPendingProducts: async function () { - for (const p of this.pendingProducts){ - p.pending = false - await this.updateProduct(p) - } - }, - customerSelectedForOrder: function (customerPubkey) { - this.$emit('customer-selected-for-order', customerPubkey) - }, - shortLabel(value = ''){ - if (value.length <= 44) return value - return value.substring(0, 40) + '...' - } - }, - created: async function () { - await this.getStall() - this.products = await this.getProducts() - this.productDialog.data = this.newEmtpyProductData() - } - }) -} diff --git a/static/components/stall-list.js b/static/components/stall-list.js new file mode 100644 index 0000000..1ef4d70 --- /dev/null +++ b/static/components/stall-list.js @@ -0,0 +1,262 @@ +window.app.component('stall-list', { + name: 'stall-list', + template: '#stall-list', + delimiters: ['${', '}'], + props: [`adminkey`, 'inkey', 'wallet-options'], + data: function () { + return { + filter: '', + stalls: [], + pendingStalls: [], + currencies: [], + stallDialog: { + show: false, + showRestore: false, + data: { + name: '', + description: '', + wallet: null, + currency: 'sat', + shippingZones: [] + } + }, + zoneOptions: [], + stallsTable: { + columns: [ + { + name: '', + align: 'left', + label: '', + field: '' + }, + { + name: 'id', + align: 'left', + label: 'Name', + field: 'id' + }, + { + name: 'currency', + align: 'left', + label: 'Currency', + field: 'currency' + }, + { + name: 'description', + align: 'left', + label: 'Description', + field: 'description' + }, + { + name: 'shippingZones', + align: 'left', + label: 'Shipping Zones', + field: 'shippingZones' + } + ], + pagination: { + rowsPerPage: 10 + } + } + } + }, + computed: { + filteredZoneOptions: function () { + return this.zoneOptions.filter( + z => z.currency === this.stallDialog.data.currency + ) + } + }, + methods: { + sendStallFormData: async function () { + const stallData = { + name: this.stallDialog.data.name, + wallet: this.stallDialog.data.wallet, + currency: this.stallDialog.data.currency, + shipping_zones: this.stallDialog.data.shippingZones, + config: { + description: this.stallDialog.data.description + } + } + if (this.stallDialog.data.id) { + stallData.id = this.stallDialog.data.id + await this.restoreStall(stallData) + } else { + await this.createStall(stallData) + } + }, + createStall: async function (stall) { + try { + const {data} = await LNbits.api.request( + 'POST', + '/nostrmarket/api/v1/stall', + this.adminkey, + stall + ) + this.stallDialog.show = false + data.expanded = false + this.stalls.unshift(data) + this.$q.notify({ + type: 'positive', + message: 'Stall created!' + }) + } catch (error) { + LNbits.utils.notifyApiError(error) + } + }, + restoreStall: async function (stallData) { + try { + stallData.pending = false + const {data} = await LNbits.api.request( + 'PUT', + `/nostrmarket/api/v1/stall/${stallData.id}`, + this.adminkey, + stallData + ) + this.stallDialog.show = false + data.expanded = false + this.stalls.unshift(data) + this.$q.notify({ + type: 'positive', + message: 'Stall restored!' + }) + } catch (error) { + LNbits.utils.notifyApiError(error) + } + }, + deleteStall: async function (pendingStall) { + LNbits.utils + .confirmDialog( + ` + Are you sure you want to delete this pending stall '${pendingStall.name}'? + ` + ) + .onOk(async () => { + try { + await LNbits.api.request( + 'DELETE', + '/nostrmarket/api/v1/stall/' + pendingStall.id, + this.adminkey + ) + this.$q.notify({ + type: 'positive', + message: 'Pending Stall Deleted', + timeout: 5000 + }) + } catch (error) { + console.warn(error) + LNbits.utils.notifyApiError(error) + } + }) + }, + getCurrencies: async function () { + try { + const {data} = await LNbits.api.request( + 'GET', + '/nostrmarket/api/v1/currencies', + this.inkey + ) + + return ['sat', ...data] + } catch (error) { + LNbits.utils.notifyApiError(error) + } + return [] + }, + getStalls: async function (pending = false) { + try { + const {data} = await LNbits.api.request( + 'GET', + `/nostrmarket/api/v1/stall?pending=${pending}`, + this.inkey + ) + return data.map(s => ({...s, expanded: false})) + } catch (error) { + LNbits.utils.notifyApiError(error) + } + return [] + }, + getZones: async function () { + try { + const {data} = await LNbits.api.request( + 'GET', + '/nostrmarket/api/v1/zone', + this.inkey + ) + return data.map(z => ({ + ...z, + label: z.name + ? `${z.name} (${z.countries.join(', ')})` + : z.countries.join(', ') + })) + } catch (error) { + LNbits.utils.notifyApiError(error) + } + return [] + }, + handleStallDeleted: function (stallId) { + this.stalls = _.reject(this.stalls, function (obj) { + return obj.id === stallId + }) + }, + handleStallUpdated: function (stall) { + const index = this.stalls.findIndex(r => r.id === stall.id) + if (index !== -1) { + stall.expanded = true + this.stalls.splice(index, 1, stall) + } + }, + openCreateStallDialog: async function (stallData) { + this.currencies = await this.getCurrencies() + this.zoneOptions = await this.getZones() + if (!this.zoneOptions || !this.zoneOptions.length) { + this.$q.notify({ + type: 'warning', + message: 'Please create a Shipping Zone first!' + }) + return + } + this.stallDialog.data = stallData || { + name: '', + description: '', + wallet: null, + currency: 'sat', + shippingZones: [] + } + this.stallDialog.show = true + }, + openSelectPendingStallDialog: async function () { + this.stallDialog.showRestore = true + this.pendingStalls = await this.getStalls(true) + }, + openRestoreStallDialog: async function (pendingStall) { + const shippingZonesIds = this.zoneOptions.map(z => z.id) + await this.openCreateStallDialog({ + id: pendingStall.id, + name: pendingStall.name, + description: pendingStall.config?.description, + currency: pendingStall.currency, + shippingZones: (pendingStall.shipping_zones || []) + .filter(z => shippingZonesIds.indexOf(z.id) !== -1) + .map(z => ({ + ...z, + label: z.name + ? `${z.name} (${z.countries.join(', ')})` + : z.countries.join(', ') + })) + }) + }, + customerSelectedForOrder: function (customerPubkey) { + this.$emit('customer-selected-for-order', customerPubkey) + }, + shortLabel(value = '') { + if (value.length <= 64) return value + return value.substring(0, 60) + '...' + } + }, + created: async function () { + this.stalls = await this.getStalls() + this.currencies = await this.getCurrencies() + this.zoneOptions = await this.getZones() + } +}) diff --git a/static/components/stall-list/stall-list.html b/static/components/stall-list/stall-list.html deleted file mode 100644 index 8981355..0000000 --- a/static/components/stall-list/stall-list.html +++ /dev/null @@ -1,117 +0,0 @@ -
-
-
- - - - - New Stall - Create a new stall - - - - - Restore Stall - Restore existing stall from Nostr - - - - - - -
-
- - - - - -
- - - - - - - - - - -
- - Cancel -
-
-
-
- - -
- - - - - - - - Restore - - - - - -
-
- There are no stalls to be restored. -
-
- Close -
-
-
-
-
\ No newline at end of file diff --git a/static/components/stall-list/stall-list.js b/static/components/stall-list/stall-list.js deleted file mode 100644 index e384895..0000000 --- a/static/components/stall-list/stall-list.js +++ /dev/null @@ -1,266 +0,0 @@ -async function stallList(path) { - const template = await loadTemplateAsync(path) - Vue.component('stall-list', { - name: 'stall-list', - template, - - props: [`adminkey`, 'inkey', 'wallet-options'], - data: function () { - return { - filter: '', - stalls: [], - pendingStalls: [], - currencies: [], - stallDialog: { - show: false, - showRestore: false, - data: { - name: '', - description: '', - wallet: null, - currency: 'sat', - shippingZones: [] - } - }, - zoneOptions: [], - stallsTable: { - columns: [ - { - name: '', - align: 'left', - label: '', - field: '' - }, - { - name: 'id', - align: 'left', - label: 'Name', - field: 'id' - }, - { - name: 'currency', - align: 'left', - label: 'Currency', - field: 'currency' - }, - { - name: 'description', - align: 'left', - label: 'Description', - field: 'description' - }, - { - name: 'shippingZones', - align: 'left', - label: 'Shipping Zones', - field: 'shippingZones' - } - ], - pagination: { - rowsPerPage: 10 - } - } - } - }, - computed: { - filteredZoneOptions: function () { - return this.zoneOptions.filter( - z => z.currency === this.stallDialog.data.currency - ) - } - }, - methods: { - sendStallFormData: async function () { - const stallData = { - name: this.stallDialog.data.name, - wallet: this.stallDialog.data.wallet, - currency: this.stallDialog.data.currency, - shipping_zones: this.stallDialog.data.shippingZones, - config: { - description: this.stallDialog.data.description - } - } - if (this.stallDialog.data.id) { - stallData.id = this.stallDialog.data.id - await this.restoreStall(stallData) - } else { - await this.createStall(stallData) - } - - }, - createStall: async function (stall) { - try { - const { data } = await LNbits.api.request( - 'POST', - '/nostrmarket/api/v1/stall', - this.adminkey, - stall - ) - this.stallDialog.show = false - data.expanded = false - this.stalls.unshift(data) - this.$q.notify({ - type: 'positive', - message: 'Stall created!' - }) - } catch (error) { - LNbits.utils.notifyApiError(error) - } - }, - restoreStall: async function (stallData) { - try { - stallData.pending = false - const { data } = await LNbits.api.request( - 'PUT', - `/nostrmarket/api/v1/stall/${stallData.id}`, - this.adminkey, - stallData - ) - this.stallDialog.show = false - data.expanded = false - this.stalls.unshift(data) - this.$q.notify({ - type: 'positive', - message: 'Stall restored!' - }) - } catch (error) { - LNbits.utils.notifyApiError(error) - } - }, - deleteStall: async function (pendingStall) { - LNbits.utils - .confirmDialog( - ` - Are you sure you want to delete this pending stall '${pendingStall.name}'? - ` - ) - .onOk(async () => { - try { - await LNbits.api.request( - 'DELETE', - '/nostrmarket/api/v1/stall/' + pendingStall.id, - this.adminkey - ) - this.$q.notify({ - type: 'positive', - message: 'Pending Stall Deleted', - timeout: 5000 - }) - } catch (error) { - console.warn(error) - LNbits.utils.notifyApiError(error) - } - }) - }, - getCurrencies: async function () { - try { - const { data } = await LNbits.api.request( - 'GET', - '/nostrmarket/api/v1/currencies', - this.inkey - ) - - return ['sat', ...data] - } catch (error) { - LNbits.utils.notifyApiError(error) - } - return [] - }, - getStalls: async function (pending = false) { - try { - const { data } = await LNbits.api.request( - 'GET', - `/nostrmarket/api/v1/stall?pending=${pending}`, - this.inkey - ) - return data.map(s => ({ ...s, expanded: false })) - } catch (error) { - LNbits.utils.notifyApiError(error) - } - return [] - }, - getZones: async function () { - try { - const { data } = await LNbits.api.request( - 'GET', - '/nostrmarket/api/v1/zone', - this.inkey - ) - return data.map(z => ({ - ...z, - label: z.name - ? `${z.name} (${z.countries.join(', ')})` - : z.countries.join(', ') - })) - } catch (error) { - LNbits.utils.notifyApiError(error) - } - return [] - }, - handleStallDeleted: function (stallId) { - this.stalls = _.reject(this.stalls, function (obj) { - return obj.id === stallId - }) - }, - handleStallUpdated: function (stall) { - const index = this.stalls.findIndex(r => r.id === stall.id) - if (index !== -1) { - stall.expanded = true - this.stalls.splice(index, 1, stall) - } - }, - openCreateStallDialog: async function (stallData) { - this.currencies = await this.getCurrencies() - this.zoneOptions = await this.getZones() - if (!this.zoneOptions || !this.zoneOptions.length) { - this.$q.notify({ - type: 'warning', - message: 'Please create a Shipping Zone first!' - }) - return - } - this.stallDialog.data = stallData || { - name: '', - description: '', - wallet: null, - currency: 'sat', - shippingZones: [] - } - this.stallDialog.show = true - }, - openSelectPendingStallDialog: async function () { - this.stallDialog.showRestore = true - this.pendingStalls = await this.getStalls(true) - }, - openRestoreStallDialog: async function (pendingStall) { - const shippingZonesIds = this.zoneOptions.map(z => z.id) - await this.openCreateStallDialog({ - id: pendingStall.id, - name: pendingStall.name, - description: pendingStall.config?.description, - currency: pendingStall.currency, - shippingZones: (pendingStall.shipping_zones || []) - .filter(z => shippingZonesIds.indexOf(z.id) !== -1) - .map(z => ({ - ...z, - label: z.name - ? `${z.name} (${z.countries.join(', ')})` - : z.countries.join(', ') - })) - }) - }, - customerSelectedForOrder: function (customerPubkey) { - this.$emit('customer-selected-for-order', customerPubkey) - }, - shortLabel(value = ''){ - if (value.length <= 64) return value - return value.substring(0, 60) + '...' - } - }, - created: async function () { - this.stalls = await this.getStalls() - this.currencies = await this.getCurrencies() - this.zoneOptions = await this.getZones() - } - }) -} diff --git a/static/js/index.js b/static/js/index.js index 4e8cf2f..3d89779 100644 --- a/static/js/index.js +++ b/static/js/index.js @@ -1,241 +1,228 @@ -const merchant = async () => { - Vue.component(VueQrcode.name, VueQrcode) +const nostr = window.NostrTools - await keyPair('static/components/key-pair/key-pair.html') - await shippingZones('static/components/shipping-zones/shipping-zones.html') - await stallDetails('static/components/stall-details/stall-details.html') - await stallList('static/components/stall-list/stall-list.html') - await orderList('static/components/order-list/order-list.html') - await directMessages('static/components/direct-messages/direct-messages.html') - await merchantDetails( - 'static/components/merchant-details/merchant-details.html' - ) - - const nostr = window.NostrTools - - new Vue({ - el: '#vue', - mixins: [windowMixin], - data: function () { - return { - merchant: {}, - shippingZones: [], - activeChatCustomer: '', - orderPubkey: null, - showKeys: false, - importKeyDialog: { - show: false, - data: { - privateKey: null - } - }, - wsConnection: null - } - }, - methods: { - generateKeys: async function () { - const privateKey = nostr.generatePrivateKey() - await this.createMerchant(privateKey) - }, - importKeys: async function () { - this.importKeyDialog.show = false - let privateKey = this.importKeyDialog.data.privateKey - if (!privateKey) { - return +window.app = Vue.createApp({ + el: '#vue', + mixins: [window.windowMixin], + data: function () { + return { + merchant: {}, + shippingZones: [], + activeChatCustomer: '', + orderPubkey: null, + showKeys: false, + importKeyDialog: { + show: false, + data: { + privateKey: null } - try { - if (privateKey.toLowerCase().startsWith('nsec')) { - privateKey = nostr.nip19.decode(privateKey).data - } - } catch (error) { - this.$q.notify({ - type: 'negative', - message: `${error}` - }) - } - await this.createMerchant(privateKey) - }, - showImportKeysDialog: async function () { - this.importKeyDialog.show = true - }, - toggleShowKeys: function () { - this.showKeys = !this.showKeys }, - toggleMerchantState: async function () { - const merchant = await this.getMerchant() - if (!merchant) { - this.$q.notify({ - timeout: 5000, - type: 'warning', - message: "Cannot fetch merchant!" - }) - return + wsConnection: null + } + }, + methods: { + generateKeys: async function () { + const privateKey = nostr.generatePrivateKey() + await this.createMerchant(privateKey) + }, + importKeys: async function () { + this.importKeyDialog.show = false + let privateKey = this.importKeyDialog.data.privateKey + if (!privateKey) { + return + } + try { + if (privateKey.toLowerCase().startsWith('nsec')) { + privateKey = nostr.nip19.decode(privateKey).data } - const message = merchant.config.active ? - 'New orders will not be processed. Are you sure you want to deactivate?' : - merchant.config.restore_in_progress ? - 'Merchant restore from nostr in progress. Please wait!! ' + - 'Activating now can lead to duplicate order processing. Click "OK" if you want to activate anyway?' : - 'Are you sure you want activate this merchant?' + } catch (error) { + this.$q.notify({ + type: 'negative', + message: `${error}` + }) + } + await this.createMerchant(privateKey) + }, + showImportKeysDialog: async function () { + this.importKeyDialog.show = true + }, + toggleShowKeys: function () { + this.showKeys = !this.showKeys + }, + toggleMerchantState: async function () { + const merchant = await this.getMerchant() + if (!merchant) { + this.$q.notify({ + timeout: 5000, + type: 'warning', + message: 'Cannot fetch merchant!' + }) + return + } + const message = merchant.config.active + ? 'New orders will not be processed. Are you sure you want to deactivate?' + : merchant.config.restore_in_progress + ? 'Merchant restore from nostr in progress. Please wait!! ' + + 'Activating now can lead to duplicate order processing. Click "OK" if you want to activate anyway?' + : 'Are you sure you want activate this merchant?' - LNbits.utils - .confirmDialog(message) - .onOk(async () => { - await this.toggleMerchant() - }) - }, - toggleMerchant: async function () { - try { - const { data } = await LNbits.api.request( - 'PUT', - `/nostrmarket/api/v1/merchant/${this.merchant.id}/toggle`, - this.g.user.wallets[0].adminkey, - ) - const state = data.config.active ? 'activated' : 'disabled' - this.merchant = data - this.$q.notify({ - type: 'positive', - message: `'Merchant ${state}`, - timeout: 5000 - }) - } catch (error) { - console.warn(error) - LNbits.utils.notifyApiError(error) + LNbits.utils.confirmDialog(message).onOk(async () => { + await this.toggleMerchant() + }) + }, + toggleMerchant: async function () { + try { + const {data} = await LNbits.api.request( + 'PUT', + `/nostrmarket/api/v1/merchant/${this.merchant.id}/toggle`, + this.g.user.wallets[0].adminkey + ) + const state = data.config.active ? 'activated' : 'disabled' + this.merchant = data + this.$q.notify({ + type: 'positive', + message: `'Merchant ${state}`, + timeout: 5000 + }) + } catch (error) { + console.warn(error) + LNbits.utils.notifyApiError(error) + } + }, + handleMerchantDeleted: function () { + this.merchant = null + this.shippingZones = [] + this.activeChatCustomer = '' + this.showKeys = false + }, + createMerchant: async function (privateKey) { + try { + const pubkey = nostr.getPublicKey(privateKey) + const payload = { + private_key: privateKey, + public_key: pubkey, + config: {} } - }, - handleMerchantDeleted: function () { - this.merchant = null - this.shippingZones = [] - this.activeChatCustomer = '' - this.showKeys = false - }, - createMerchant: async function (privateKey) { - try { - const pubkey = nostr.getPublicKey(privateKey) - const payload = { - private_key: privateKey, - public_key: pubkey, - config: {} + const {data} = await LNbits.api.request( + 'POST', + '/nostrmarket/api/v1/merchant', + this.g.user.wallets[0].adminkey, + payload + ) + this.merchant = data + this.$q.notify({ + type: 'positive', + message: 'Merchant Created!' + }) + this.waitForNotifications() + } catch (error) { + LNbits.utils.notifyApiError(error) + } + }, + getMerchant: async function () { + try { + const {data} = await LNbits.api.request( + 'GET', + '/nostrmarket/api/v1/merchant', + this.g.user.wallets[0].inkey + ) + this.merchant = data + return data + } catch (error) { + LNbits.utils.notifyApiError(error) + } + }, + customerSelectedForOrder: function (customerPubkey) { + this.activeChatCustomer = customerPubkey + }, + filterOrdersForCustomer: function (customerPubkey) { + this.orderPubkey = customerPubkey + }, + showOrderDetails: async function (orderData) { + await this.$refs.orderListRef.orderSelected( + orderData.orderId, + orderData.eventId + ) + }, + waitForNotifications: async function () { + if (!this.merchant) return + try { + const scheme = location.protocol === 'http:' ? 'ws' : 'wss' + const port = location.port ? `:${location.port}` : '' + const wsUrl = `${scheme}://${document.domain}${port}/api/v1/ws/${this.merchant.id}` + console.log('Reconnecting to websocket: ', wsUrl) + this.wsConnection = new WebSocket(wsUrl) + this.wsConnection.onmessage = async e => { + const data = JSON.parse(e.data) + if (data.type === 'dm:0') { + this.$q.notify({ + timeout: 5000, + type: 'positive', + message: 'New Order' + }) + + await this.$refs.directMessagesRef.handleNewMessage(data) + return } - const { data } = await LNbits.api.request( - 'POST', - '/nostrmarket/api/v1/merchant', - this.g.user.wallets[0].adminkey, - payload - ) - this.merchant = data - this.$q.notify({ - type: 'positive', - message: 'Merchant Created!' - }) - this.waitForNotifications() - } catch (error) { - LNbits.utils.notifyApiError(error) - } - }, - getMerchant: async function () { - try { - const { data } = await LNbits.api.request( - 'GET', - '/nostrmarket/api/v1/merchant', - this.g.user.wallets[0].inkey - ) - this.merchant = data - return data - } catch (error) { - LNbits.utils.notifyApiError(error) - } - }, - customerSelectedForOrder: function (customerPubkey) { - this.activeChatCustomer = customerPubkey - }, - filterOrdersForCustomer: function (customerPubkey) { - this.orderPubkey = customerPubkey - }, - showOrderDetails: async function (orderData) { - await this.$refs.orderListRef.orderSelected(orderData.orderId, orderData.eventId) - }, - waitForNotifications: async function () { - if (!this.merchant) return - try { - const scheme = location.protocol === 'http:' ? 'ws' : 'wss' - const port = location.port ? `:${location.port}` : '' - const wsUrl = `${scheme}://${document.domain}${port}/api/v1/ws/${this.merchant.id}` - console.log('Reconnecting to websocket: ', wsUrl) - this.wsConnection = new WebSocket(wsUrl) - this.wsConnection.onmessage = async e => { - const data = JSON.parse(e.data) - if (data.type === 'dm:0') { - this.$q.notify({ - timeout: 5000, - type: 'positive', - message: 'New Order' - }) - - await this.$refs.directMessagesRef.handleNewMessage(data) - return - } - if (data.type === 'dm:1') { - await this.$refs.directMessagesRef.handleNewMessage(data) - await this.$refs.orderListRef.addOrder(data) - return - } - if (data.type === 'dm:2') { - const orderStatus = JSON.parse(data.dm.message) - this.$q.notify({ - timeout: 5000, - type: 'positive', - message: orderStatus.message - }) - if (orderStatus.paid) { - await this.$refs.orderListRef.orderPaid(orderStatus.id) - } - await this.$refs.directMessagesRef.handleNewMessage(data) - return - } - if (data.type === 'dm:-1') { - await this.$refs.directMessagesRef.handleNewMessage(data) + if (data.type === 'dm:1') { + await this.$refs.directMessagesRef.handleNewMessage(data) + await this.$refs.orderListRef.addOrder(data) + return + } + if (data.type === 'dm:2') { + const orderStatus = JSON.parse(data.dm.message) + this.$q.notify({ + timeout: 5000, + type: 'positive', + message: orderStatus.message + }) + if (orderStatus.paid) { + await this.$refs.orderListRef.orderPaid(orderStatus.id) } - // order paid - // order shipped + await this.$refs.directMessagesRef.handleNewMessage(data) + return } - - } catch (error) { - this.$q.notify({ - timeout: 5000, - type: 'warning', - message: 'Failed to watch for updates', - caption: `${error}` - }) + if (data.type === 'dm:-1') { + await this.$refs.directMessagesRef.handleNewMessage(data) + } + // order paid + // order shipped } - }, - restartNostrConnection: async function () { - LNbits.utils - .confirmDialog( - 'Are you sure you want to reconnect to the nostrcient extension?' - ) - .onOk(async () => { - try { - await LNbits.api.request( - 'PUT', - '/nostrmarket/api/v1/restart', - this.g.user.wallets[0].adminkey - ) - } catch (error) { - LNbits.utils.notifyApiError(error) - } - }) + } catch (error) { + this.$q.notify({ + timeout: 5000, + type: 'warning', + message: 'Failed to watch for updates', + caption: `${error}` + }) } }, - created: async function () { - await this.getMerchant() - setInterval(async () => { - if (!this.wsConnection || this.wsConnection.readyState !== WebSocket.OPEN) { - await this.waitForNotifications() - } - }, 1000) + restartNostrConnection: async function () { + LNbits.utils + .confirmDialog( + 'Are you sure you want to reconnect to the nostrcient extension?' + ) + .onOk(async () => { + try { + await LNbits.api.request( + 'PUT', + '/nostrmarket/api/v1/restart', + this.g.user.wallets[0].adminkey + ) + } catch (error) { + LNbits.utils.notifyApiError(error) + } + }) } - }) -} - -merchant() + }, + created: async function () { + await this.getMerchant() + setInterval(async () => { + if ( + !this.wsConnection || + this.wsConnection.readyState !== WebSocket.OPEN + ) { + await this.waitForNotifications() + } + }, 1000) + } +}) diff --git a/static/js/utils.js b/static/js/utils.js index a28c455..b244fce 100644 --- a/static/js/utils.js +++ b/static/js/utils.js @@ -23,7 +23,7 @@ function imgSizeFit(img, maxWidth = 1024, maxHeight = 768) { maxWidth / img.naturalWidth, maxHeight / img.naturalHeight ) - return { width: img.naturalWidth * ratio, height: img.naturalHeight * ratio } + return {width: img.naturalWidth * ratio, height: img.naturalHeight * ratio} } async function hash(string) { @@ -125,7 +125,7 @@ function isValidImageUrl(string) { function isValidKey(key, prefix = 'n') { try { if (key && key.startsWith(prefix)) { - let { _, data } = NostrTools.nip19.decode(key) + let {_, data} = NostrTools.nip19.decode(key) key = data } return isValidKeyHex(key) @@ -143,4 +143,4 @@ function formatCurrency(value, currency) { style: 'currency', currency: currency }).format(value) -} \ No newline at end of file +} diff --git a/static/market/index.html b/static/market/index.html index 4b4e656..381d551 100644 --- a/static/market/index.html +++ b/static/market/index.html @@ -11,11 +11,11 @@ - - - - - + + + + + diff --git a/tasks.py b/tasks.py index 4f50ebe..013a281 100644 --- a/tasks.py +++ b/tasks.py @@ -1,10 +1,9 @@ -from asyncio import Queue import asyncio - -from loguru import logger +from asyncio import Queue from lnbits.core.models import Payment from lnbits.tasks import register_invoice_listener +from loguru import logger from .nostr.nostr_client import NostrClient from .services import ( diff --git a/templates/nostrmarket/components/direct-messages.html b/templates/nostrmarket/components/direct-messages.html new file mode 100644 index 0000000..8950923 --- /dev/null +++ b/templates/nostrmarket/components/direct-messages.html @@ -0,0 +1,169 @@ +
+ + +
+
+
Messages
+
+
+   new +
+
+ Client Orders +
+
+
+ + + + +
+
+ + +
+
+ + Add a public key to chat with + +
+
+
+ +
+
+
+ +
+
+ New order: +
+
+ Reply sent for order: +
+
+ Paid + Shipped + +
+
+ + + + +
+ ... +
+
+
+
+
+ + + + + + + +
+
+
+
+ + + + +
+ Add + Cancel +
+
+
+
+ + + + +
+ Close +
+
+
+
+
+
diff --git a/static/components/key-pair/key-pair.html b/templates/nostrmarket/components/key-pair.html similarity index 84% rename from static/components/key-pair/key-pair.html rename to templates/nostrmarket/components/key-pair.html index a0657fa..2b65737 100644 --- a/static/components/key-pair/key-pair.html +++ b/templates/nostrmarket/components/key-pair.html @@ -13,11 +13,11 @@
- + >
Click to copy
@@ -30,11 +30,7 @@ class="q-mx-xl" @click="copyText(privateKey)" > - +
Click to copy
diff --git a/static/components/merchant-details/merchant-details.html b/templates/nostrmarket/components/merchant-details.html similarity index 100% rename from static/components/merchant-details/merchant-details.html rename to templates/nostrmarket/components/merchant-details.html diff --git a/templates/nostrmarket/components/order-list.html b/templates/nostrmarket/components/order-list.html new file mode 100644 index 0000000..13e684f --- /dev/null +++ b/templates/nostrmarket/components/order-list.html @@ -0,0 +1,369 @@ +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + + + + Restore Orders + Restore previous orders from Nostr + + + +
+
+
+
+ + + +
+
+ + + + + +
+ + + Cancel +
+
+
+
+
diff --git a/static/components/shipping-zones/shipping-zones.html b/templates/nostrmarket/components/shipping-zones.html similarity index 91% rename from static/components/shipping-zones/shipping-zones.html rename to templates/nostrmarket/components/shipping-zones.html index 04f5650..3f0fd08 100644 --- a/static/components/shipping-zones/shipping-zones.html +++ b/templates/nostrmarket/components/shipping-zones.html @@ -22,12 +22,13 @@ @click="openZoneDialog(zone)" > - {{zone.name}} - {{zone.countries.join(", ")}} + + - - + diff --git a/templates/nostrmarket/components/stall-details.html b/templates/nostrmarket/components/stall-details.html new file mode 100644 index 0000000..aa67673 --- /dev/null +++ b/templates/nostrmarket/components/stall-details.html @@ -0,0 +1,466 @@ +
+ + + + + + + +
+
+
ID:
+
+ +
+
+
+
+
Name:
+
+ +
+
+
+
+
Description:
+
+ +
+
+
+
+
Wallet:
+
+ + +
+
+
+
+
Currency:
+
+ +
+
+
+
+
Shipping Zones:
+
+ +
+
+
+
+
+
+ Update Stall +
+
+ Delete Stall +
+
+
+ +
+
+
+ + + + New Product + Create a new product + + + + + Restore Product + Restore existing product from Nostr + + + +
+
+
+
+ +
+
+ + + +
+
+
+
+ +
+ +
+
+
+ + + + + + + +
+
+ +
+
+ +
+
+ + +
+ +
+
+ + +
+ + + + + + +
+
+ + +
+
+ +
+
+ + +
+
+
+ + + +
+
+ +
+
+ +
+
+ + +
+
+
+
+
+ +
+ + + Create Product + + Cancel +
+
+
+
+ + +
+ + + + + + + + Restore + + + + + +
+
There are no products to be restored.
+
+ Restore All + Close +
+
+
+
diff --git a/templates/nostrmarket/components/stall-list.html b/templates/nostrmarket/components/stall-list.html new file mode 100644 index 0000000..632eeaa --- /dev/null +++ b/templates/nostrmarket/components/stall-list.html @@ -0,0 +1,215 @@ +
+
+
+ + + + New Stall + Create a new stall + + + + + Restore Stall + Restore existing stall from Nostr + + + + + + +
+
+ + + + + +
+ + + + + + + + + + +
+ + Cancel +
+
+
+
+ + +
+ + + + + + + + Restore + + + + + +
+
There are no stalls to be restored.
+
+ Close +
+
+
+
+
diff --git a/templates/nostrmarket/index.html b/templates/nostrmarket/index.html index e7296c3..9b1cafc 100644 --- a/templates/nostrmarket/index.html +++ b/templates/nostrmarket/index.html @@ -18,14 +18,17 @@
+ @update:model-value="toggleMerchantState()" + size="md" + checked-icon="check" + v-model="merchant.config.active" + color="primary" + unchecked-icon="clear" + class="float-left" + /> +
} + + + + + + + + - - - - - - - + + + + + + + + {% endblock %} diff --git a/templates/nostrmarket/market.html b/templates/nostrmarket/market.html index b03a7a4..4c72d1a 100644 --- a/templates/nostrmarket/market.html +++ b/templates/nostrmarket/market.html @@ -1,36 +1,59 @@ - + - - + Nostr Market App - - - - - - + + + + + - + - - - + + + - - - - - -
- + + + - \ No newline at end of file + +
+ + diff --git a/toc.md b/toc.md index 544be53..baa0342 100644 --- a/toc.md +++ b/toc.md @@ -1,22 +1,29 @@ # Terms and Conditions for LNbits Extension ## 1. Acceptance of Terms + By installing and using the LNbits extension ("Extension"), you agree to be bound by these terms and conditions ("Terms"). If you do not agree to these Terms, do not use the Extension. ## 2. License + The Extension is free and open-source software, released under [specify the FOSS license here, e.g., GPL-3.0, MIT, etc.]. You are permitted to use, copy, modify, and distribute the Extension under the terms of that license. ## 3. No Warranty + The Extension is provided "as is" and with all faults, and the developer expressly disclaims all warranties of any kind, whether express, implied, statutory, or otherwise, including but not limited to warranties of merchantability, fitness for a particular purpose, non-infringement, and any warranties arising out of course of dealing or usage of trade. No advice or information, whether oral or written, obtained from the developer or elsewhere will create any warranty not expressly stated in this Terms. ## 4. Limitation of Liability + In no event will the developer be liable to you or any third party for any direct, indirect, incidental, special, consequential, or punitive damages, including lost profit, lost revenue, loss of data, or other damages arising out of or in connection with your use of the Extension, even if the developer has been advised of the possibility of such damages. The foregoing limitation of liability shall apply to the fullest extent permitted by law in the applicable jurisdiction. ## 5. Modification of Terms + The developer reserves the right to modify these Terms at any time. You are advised to review these Terms periodically for any changes. Changes to these Terms are effective when they are posted on the appropriate location within or associated with the Extension. ## 6. General Provisions + If any provision of these Terms is held to be invalid or unenforceable, that provision will be enforced to the maximum extent permissible, and the other provisions of these Terms will remain in full force and effect. These Terms constitute the entire agreement between you and the developer regarding the use of the Extension. ## 7. Contact Information -If you have any questions about these Terms, please contact the developer at [developer's contact information]. \ No newline at end of file + +If you have any questions about these Terms, please contact the developer at [developer's contact information]. diff --git a/views.py b/views.py index 3b757fb..4c3e5e7 100644 --- a/views.py +++ b/views.py @@ -1,13 +1,8 @@ -import json -from http import HTTPStatus - -from fastapi import Depends, Query, Request +from fastapi import Depends, Request from fastapi.templating import Jinja2Templates -from loguru import logger -from starlette.responses import HTMLResponse - from lnbits.core.models import User from lnbits.decorators import check_user_exists +from starlette.responses import HTMLResponse from . import nostrmarket_ext, nostrmarket_renderer @@ -18,7 +13,7 @@ async def index(request: Request, user: User = Depends(check_user_exists)): return nostrmarket_renderer().TemplateResponse( "nostrmarket/index.html", - {"request": request, "user": user.dict()}, + {"request": request, "user": user.json()}, ) diff --git a/views_api.py b/views_api.py index 9f77217..b8019fa 100644 --- a/views_api.py +++ b/views_api.py @@ -4,16 +4,14 @@ from fastapi import Depends from fastapi.exceptions import HTTPException -from loguru import logger - from lnbits.core.services import websocket_updater from lnbits.decorators import ( WalletTypeInfo, - get_key_type, require_admin_key, require_invoice_key, ) from lnbits.utils.exchange_rates import currencies +from loguru import logger from . import nostr_client, nostrmarket_ext from .crud import ( @@ -71,9 +69,6 @@ PartialDirectMessage, PartialMerchant, PartialOrder, - PartialProduct, - PartialStall, - PartialZone, PaymentOption, PaymentRequest, Product, @@ -81,16 +76,16 @@ Zone, ) from .services import ( - reply_to_structured_dm, build_order_with_payment, create_or_update_order_from_dm, + reply_to_structured_dm, resubscribe_to_all_merchants, sign_and_send_to_nostr, subscribe_to_all_merchants, update_merchant_to_nostr, ) -######################################## MERCHANT ######################################## +######################################## MERCHANT ###################################### @nostrmarket_ext.post("/api/v1/merchant") @@ -101,16 +96,16 @@ async def api_create_merchant( try: merchant = await get_merchant_by_pubkey(data.public_key) - assert merchant == None, "A merchant already uses this public key" + assert merchant is None, "A merchant already uses this public key" merchant = await get_merchant_for_user(wallet.wallet.user) - assert merchant == None, "A merchant already exists for this user" + assert merchant is None, "A merchant already exists for this user" merchant = await create_merchant(wallet.wallet.user, data) await create_zone( merchant.id, - PartialZone( + Zone( id=f"online-{merchant.public_key}", name="Online", currency="sat", @@ -128,13 +123,13 @@ async def api_create_merchant( raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail=str(ex), - ) + ) from ex except Exception as ex: logger.warning(ex) raise HTTPException( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Cannot create merchant", - ) + ) from ex @nostrmarket_ext.get("/api/v1/merchant") @@ -145,11 +140,12 @@ async def api_get_merchant( try: merchant = await get_merchant_for_user(wallet.wallet.user) if not merchant: - return + return None merchant = await touch_merchant(wallet.wallet.user, merchant.id) + assert merchant last_dm_time = await get_last_direct_messages_time(merchant.id) - + assert merchant.time merchant.config.restore_in_progress = (merchant.time - last_dm_time) < 30 return merchant @@ -158,7 +154,7 @@ async def api_get_merchant( raise HTTPException( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Cannot get merchant", - ) + ) from ex @nostrmarket_ext.delete("/api/v1/merchant/{merchant_id}") @@ -186,16 +182,17 @@ async def api_delete_merchant( raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail=str(ex), - ) + ) from ex except Exception as ex: logger.warning(ex) raise HTTPException( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Cannot get merchant", - ) + ) from ex finally: await subscribe_to_all_merchants() + @nostrmarket_ext.put("/api/v1/merchant/{merchant_id}/nostr") async def api_republish_merchant( merchant_id: str, @@ -213,13 +210,14 @@ async def api_republish_merchant( raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail=str(ex), - ) + ) from ex except Exception as ex: logger.warning(ex) raise HTTPException( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Cannot republish to nostr", - ) + ) from ex + @nostrmarket_ext.get("/api/v1/merchant/{merchant_id}/nostr") async def api_refresh_merchant( @@ -237,13 +235,13 @@ async def api_refresh_merchant( raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail=str(ex), - ) + ) from ex except Exception as ex: logger.warning(ex) raise HTTPException( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Cannot refresh from nostr", - ) + ) from ex @nostrmarket_ext.put("/api/v1/merchant/{merchant_id}/toggle") @@ -264,17 +262,17 @@ async def api_toggle_merchant( raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail=str(ex), - ) + ) from ex except Exception as ex: logger.warning(ex) raise HTTPException( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Cannot get merchant", - ) + ) from ex @nostrmarket_ext.delete("/api/v1/merchant/{merchant_id}/nostr") -async def api_delete_merchant( +async def api_delete_merchant_on_nostr( merchant_id: str, wallet: WalletTypeInfo = Depends(require_admin_key), ): @@ -290,20 +288,22 @@ async def api_delete_merchant( raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail=str(ex), - ) + ) from ex except Exception as ex: logger.warning(ex) raise HTTPException( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Cannot get merchant", - ) + ) from ex ######################################## ZONES ######################################## @nostrmarket_ext.get("/api/v1/zone") -async def api_get_zones(wallet: WalletTypeInfo = Depends(get_key_type)) -> List[Zone]: +async def api_get_zones( + wallet: WalletTypeInfo = Depends(require_invoice_key), +) -> List[Zone]: try: merchant = await get_merchant_for_user(wallet.wallet.user) assert merchant, "Merchant cannot be found" @@ -312,18 +312,18 @@ async def api_get_zones(wallet: WalletTypeInfo = Depends(get_key_type)) -> List[ raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail=str(ex), - ) + ) from ex except Exception as ex: logger.warning(ex) raise HTTPException( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Cannot get zone", - ) + ) from ex @nostrmarket_ext.post("/api/v1/zone") async def api_create_zone( - data: PartialZone, wallet: WalletTypeInfo = Depends(require_admin_key) + data: Zone, wallet: WalletTypeInfo = Depends(require_admin_key) ): try: merchant = await get_merchant_for_user(wallet.wallet.user) @@ -334,13 +334,13 @@ async def api_create_zone( raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail=str(ex), - ) + ) from ex except Exception as ex: logger.warning(ex) raise HTTPException( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Cannot create zone", - ) + ) from ex @nostrmarket_ext.patch("/api/v1/zone/{zone_id}") @@ -365,15 +365,14 @@ async def api_update_zone( raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail=str(ex), - ) - except HTTPException as ex: - raise ex + ) from ex + except Exception as ex: logger.warning(ex) raise HTTPException( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Cannot update zone", - ) + ) from ex @nostrmarket_ext.delete("/api/v1/zone/{zone_id}") @@ -394,13 +393,13 @@ async def api_delete_zone(zone_id, wallet: WalletTypeInfo = Depends(require_admi raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail=str(ex), - ) + ) from ex except Exception as ex: logger.warning(ex) raise HTTPException( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Cannot delete zone", - ) + ) from ex ######################################## STALLS ######################################## @@ -408,7 +407,7 @@ async def api_delete_zone(zone_id, wallet: WalletTypeInfo = Depends(require_admi @nostrmarket_ext.post("/api/v1/stall") async def api_create_stall( - data: PartialStall, + data: Stall, wallet: WalletTypeInfo = Depends(require_admin_key), ) -> Stall: try: @@ -430,13 +429,13 @@ async def api_create_stall( raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail=str(ex), - ) + ) from ex except Exception as ex: logger.warning(ex) raise HTTPException( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Cannot create stall", - ) + ) from ex @nostrmarket_ext.put("/api/v1/stall/{stall_id}") @@ -459,23 +458,24 @@ async def api_update_stall( await update_stall(merchant.id, stall) return stall - except HTTPException as ex: - raise ex + except (ValueError, AssertionError) as ex: raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail=str(ex), - ) + ) from ex except Exception as ex: logger.warning(ex) raise HTTPException( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Cannot update stall", - ) + ) from ex @nostrmarket_ext.get("/api/v1/stall/{stall_id}") -async def api_get_stall(stall_id: str, wallet: WalletTypeInfo = Depends(get_key_type)): +async def api_get_stall( + stall_id: str, wallet: WalletTypeInfo = Depends(require_invoice_key) +): try: merchant = await get_merchant_for_user(wallet.wallet.user) assert merchant, "Merchant cannot be found" @@ -490,7 +490,7 @@ async def api_get_stall(stall_id: str, wallet: WalletTypeInfo = Depends(get_key_ raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail=str(ex), - ) + ) from ex except HTTPException as ex: raise ex except Exception as ex: @@ -498,12 +498,13 @@ async def api_get_stall(stall_id: str, wallet: WalletTypeInfo = Depends(get_key_ raise HTTPException( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Cannot get stall", - ) + ) from ex @nostrmarket_ext.get("/api/v1/stall") async def api_get_stalls( - pending: Optional[bool] = False, wallet: WalletTypeInfo = Depends(get_key_type) + pending: Optional[bool] = False, + wallet: WalletTypeInfo = Depends(require_invoice_key), ): try: merchant = await get_merchant_for_user(wallet.wallet.user) @@ -514,13 +515,13 @@ async def api_get_stalls( raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail=str(ex), - ) + ) from ex except Exception as ex: logger.warning(ex) raise HTTPException( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Cannot get stalls", - ) + ) from ex @nostrmarket_ext.get("/api/v1/stall/product/{stall_id}") @@ -538,13 +539,13 @@ async def api_get_stall_products( raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail=str(ex), - ) + ) from ex except Exception as ex: logger.warning(ex) raise HTTPException( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Cannot get stall products", - ) + ) from ex @nostrmarket_ext.get("/api/v1/stall/order/{stall_id}") @@ -566,13 +567,13 @@ async def api_get_stall_orders( raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail=str(ex), - ) + ) from ex except Exception as ex: logger.warning(ex) raise HTTPException( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Cannot get stall products", - ) + ) from ex @nostrmarket_ext.delete("/api/v1/stall/{stall_id}") @@ -600,23 +601,21 @@ async def api_delete_stall( raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail=str(ex), - ) - except HTTPException as ex: - raise ex + ) from ex except Exception as ex: logger.warning(ex) raise HTTPException( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Cannot delete stall", - ) + ) from ex -######################################## PRODUCTS ######################################## +######################################## PRODUCTS ###################################### @nostrmarket_ext.post("/api/v1/product") async def api_create_product( - data: PartialProduct, + data: Product, wallet: WalletTypeInfo = Depends(require_admin_key), ) -> Product: try: @@ -639,13 +638,13 @@ async def api_create_product( raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail=str(ex), - ) + ) from ex except Exception as ex: logger.warning(ex) raise HTTPException( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Cannot create product", - ) + ) from ex @nostrmarket_ext.patch("/api/v1/product/{product_id}") @@ -675,13 +674,13 @@ async def api_update_product( raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail=str(ex), - ) + ) from ex except Exception as ex: logger.warning(ex) raise HTTPException( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Cannot update product", - ) + ) from ex @nostrmarket_ext.get("/api/v1/product/{product_id}") @@ -699,13 +698,13 @@ async def api_get_product( raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail=str(ex), - ) + ) from ex except Exception as ex: logger.warning(ex) raise HTTPException( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Cannot get product", - ) + ) from ex @nostrmarket_ext.delete("/api/v1/product/{product_id}") @@ -731,15 +730,13 @@ async def api_delete_product( raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail=str(ex), - ) - except HTTPException as ex: - raise ex + ) from ex except Exception as ex: logger.warning(ex) raise HTTPException( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Cannot delete product", - ) + ) from ex ######################################## ORDERS ######################################## @@ -764,15 +761,13 @@ async def api_get_order( raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail=str(ex), - ) - except HTTPException as ex: - raise ex + ) from ex except Exception as ex: logger.warning(ex) raise HTTPException( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Cannot get order", - ) + ) from ex @nostrmarket_ext.get("/api/v1/order") @@ -780,7 +775,7 @@ async def api_get_orders( paid: Optional[bool] = None, shipped: Optional[bool] = None, pubkey: Optional[str] = None, - wallet: WalletTypeInfo = Depends(get_key_type), + wallet: WalletTypeInfo = Depends(require_invoice_key), ): try: merchant = await get_merchant_for_user(wallet.wallet.user) @@ -794,13 +789,13 @@ async def api_get_orders( raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail=str(ex), - ) + ) from ex except Exception as ex: logger.warning(ex) raise HTTPException( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Cannot get orders", - ) + ) from ex @nostrmarket_ext.patch("/api/v1/order/{order_id}") @@ -809,7 +804,7 @@ async def api_update_order_status( wallet: WalletTypeInfo = Depends(require_admin_key), ) -> Order: try: - assert data.shipped != None, "Shipped value is required for order" + assert data.shipped is not None, "Shipped value is required for order" merchant = await get_merchant_for_user(wallet.wallet.user) assert merchant, "Merchant cannot be found for order {data.id}" @@ -852,20 +847,20 @@ async def api_update_order_status( raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail=str(ex), - ) + ) from ex except Exception as ex: logger.warning(ex) raise HTTPException( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Cannot update order", - ) + ) from ex @nostrmarket_ext.put("/api/v1/order/restore/{event_id}") async def api_restore_order( event_id: str, wallet: WalletTypeInfo = Depends(require_admin_key), -) -> Order: +) -> Optional[Order]: try: merchant = await get_merchant_for_user(wallet.wallet.user) assert merchant, "Merchant cannot be found" @@ -881,13 +876,13 @@ async def api_restore_order( raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail=str(ex), - ) + ) from ex except Exception as ex: logger.warning(ex) raise HTTPException( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Cannot restore order", - ) + ) from ex @nostrmarket_ext.put("/api/v1/orders/restore") @@ -906,20 +901,20 @@ async def api_restore_orders( ) except Exception as e: logger.debug( - f"Failed to restore order from event '{dm.event_id}': '{str(e)}'." + f"Failed to restore order from event '{dm.event_id}': '{e!s}'." ) except AssertionError as ex: raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail=str(ex), - ) + ) from ex except Exception as ex: logger.warning(ex) raise HTTPException( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Cannot restore orders", - ) + ) from ex @nostrmarket_ext.put("/api/v1/order/reissue") @@ -955,7 +950,9 @@ async def api_reissue_order_invoice( **order_update, ) payment_req = PaymentRequest( - id=data.id, payment_options=[PaymentOption(type="ln", link=invoice)], message=receipt + id=data.id, + payment_options=[PaymentOption(type="ln", link=invoice)], + message=receipt, ) response = { "type": DirectMessageType.PAYMENT_REQUEST.value, @@ -975,25 +972,25 @@ async def api_reissue_order_invoice( raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail=str(ex), - ) + ) from ex except Exception as ex: logger.warning(ex) raise HTTPException( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Cannot reissue order invoice", - ) + ) from ex -######################################## DIRECT MESSAGES ######################################## +######################################## DIRECT MESSAGES ############################### @nostrmarket_ext.get("/api/v1/message/{public_key}") async def api_get_messages( - public_key: str, wallet: WalletTypeInfo = Depends(get_key_type) + public_key: str, wallet: WalletTypeInfo = Depends(require_invoice_key) ) -> List[DirectMessage]: try: merchant = await get_merchant_for_user(wallet.wallet.user) - assert merchant, f"Merchant cannot be found" + assert merchant, "Merchant cannot be found" messages = await get_direct_messages(merchant.id, public_key) await update_customer_no_unread_messages(merchant.id, public_key) @@ -1002,13 +999,13 @@ async def api_get_messages( raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail=str(ex), - ) + ) from ex except Exception as ex: logger.warning(ex) raise HTTPException( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Cannot get direct message", - ) + ) from ex @nostrmarket_ext.post("/api/v1/message") @@ -1017,7 +1014,7 @@ async def api_create_message( ) -> DirectMessage: try: merchant = await get_merchant_for_user(wallet.wallet.user) - assert merchant, f"Merchant cannot be found" + assert merchant, "Merchant cannot be found" dm_event = merchant.build_dm_event(data.message, data.public_key) data.event_id = dm_event.id @@ -1031,38 +1028,38 @@ async def api_create_message( raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail=str(ex), - ) + ) from ex except Exception as ex: logger.warning(ex) raise HTTPException( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Cannot create message", - ) + ) from ex -######################################## CUSTOMERS ######################################## +######################################## CUSTOMERS ##################################### @nostrmarket_ext.get("/api/v1/customer") async def api_get_customers( - wallet: WalletTypeInfo = Depends(get_key_type), + wallet: WalletTypeInfo = Depends(require_invoice_key), ) -> List[Customer]: try: merchant = await get_merchant_for_user(wallet.wallet.user) - assert merchant, f"Merchant cannot be found" + assert merchant, "Merchant cannot be found" return await get_customers(merchant.id) except AssertionError as ex: raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail=str(ex), - ) + ) from ex except Exception as ex: logger.warning(ex) raise HTTPException( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Cannot create message", - ) + ) from ex @nostrmarket_ext.post("/api/v1/customer") @@ -1079,7 +1076,7 @@ async def api_create_customer( assert merchant.id == data.merchant_id, "Invalid merchant id for user" existing_customer = await get_customer(merchant.id, pubkey) - assert existing_customer == None, "This public key already exists" + assert existing_customer is None, "This public key already exists" customer = await create_customer( merchant.id, Customer(merchant_id=merchant.id, public_key=pubkey) @@ -1092,13 +1089,13 @@ async def api_create_customer( raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail=str(ex), - ) + ) from ex except Exception as ex: logger.warning(ex) raise HTTPException( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Cannot create customer", - ) + ) from ex ######################################## OTHER ########################################