diff --git a/.env.example b/.env.example
index 6a3710c2..56943c4d 100644
--- a/.env.example
+++ b/.env.example
@@ -54,8 +54,9 @@ LNBITS_SITE_DESCRIPTION="Some description about your service, will display if ti
LNBITS_THEME_OPTIONS="classic, bitcoin, flamingo, freedom, mint, autumn, monochrome, salvador"
# LNBITS_CUSTOM_LOGO="https://lnbits.com/assets/images/logo/logo.svg"
-# Choose from LNPayWallet, OpenNodeWallet, LntxbotWallet, ClicheWallet, LnTipsWallet
-# LndRestWallet, CoreLightningWallet, LNbitsWallet, SparkWallet, FakeWallet, EclairWallet
+# Choose from LNPayWallet, OpenNodeWallet, LntxbotWallet, ClicheWallet,
+# LndWallet, LndRestWallet, CoreLightningWallet, EclairWallet,
+# LnTipsWallet, LNbitsWallet, SparkWallet, FakeWallet,
LNBITS_BACKEND_WALLET_CLASS=VoidWallet
# VoidWallet is just a fallback that works without any actual Lightning capabilities,
# just so you can see the UI before dealing with this file.
@@ -76,6 +77,14 @@ CORELIGHTNING_RPC="/home/bob/.lightning/bitcoin/lightning-rpc"
LNBITS_ENDPOINT=https://legend.lnbits.com
LNBITS_KEY=LNBITS_ADMIN_KEY
+# LndWallet
+LND_GRPC_ENDPOINT=127.0.0.1
+LND_GRPC_PORT=10009
+LND_GRPC_CERT="/home/bob/.config/Zap/lnd/bitcoin/mainnet/wallet-1/data/chain/bitcoin/mainnet/tls.cert"
+LND_GRPC_MACAROON="/home/bob/.config/Zap/lnd/bitcoin/mainnet/wallet-1/data/chain/bitcoin/mainnet/admin.macaroon or HEXSTRING"
+# To use an AES-encrypted macaroon, set
+# LND_GRPC_MACAROON="eNcRyPtEdMaCaRoOn"
+
# LndRestWallet
LND_REST_ENDPOINT=https://127.0.0.1:8080/
LND_REST_CERT="/home/bob/.config/Zap/lnd/bitcoin/mainnet/wallet-1/data/chain/bitcoin/mainnet/tls.cert"
diff --git a/docs/guide/installation.md b/docs/guide/installation.md
index 2bbdfb11..52ce6420 100644
--- a/docs/guide/installation.md
+++ b/docs/guide/installation.md
@@ -15,8 +15,8 @@ By default, LNbits will use SQLite as its database. You can also use PostgreSQL
If you have problems installing LNbits using these instructions, please have a look at the [Troubleshooting](#troubleshooting) section.
```sh
-git clone https://github.com/lnbits/lnbits-legend.git
-cd lnbits-legend/
+git clone https://github.com/lnbits/lnbits.git
+cd lnbits
# for making sure python 3.9 is installed, skip if installed. To check your installed version: python3 --version
sudo apt update
@@ -50,9 +50,10 @@ poetry run lnbits
#### Updating the server
```
-cd lnbits-legend/
+cd lnbits
# Stop LNbits with `ctrl + x`
git pull
+# Keep your poetry install up to date, this can be done with `poetry self update`
poetry install --only main
# Start LNbits with `poetry run lnbits`
```
@@ -62,8 +63,8 @@ poetry install --only main
> note: currently not supported while we make some architectural changes on the path to leave beta
```sh
-git clone https://github.com/lnbits/lnbits-legend.git
-cd lnbits-legend/
+git clone https://github.com/lnbits/lnbits.git
+cd lnbits
# Modern debian distros usually include Nix, however you can install with:
# 'sh <(curl -L https://nixos.org/nix/install) --daemon', or use setup here https://nixos.org/download.html#nix-verify-installation
@@ -82,8 +83,8 @@ LNBITS_DATA_FOLDER=data LNBITS_BACKEND_WALLET_CLASS=LNbitsWallet LNBITS_ENDPOINT
## Option 3: venv
```sh
-git clone https://github.com/lnbits/lnbits-legend.git
-cd lnbits-legend/
+git clone https://github.com/lnbits/lnbits.git
+cd lnbits
# ensure you have virtualenv installed, on debian/ubuntu 'apt install python3.9-venv'
python3.9 -m venv venv
# If you have problems here, try `sudo apt install -y pkg-config libpq-dev`
@@ -105,9 +106,9 @@ If you want to host LNbits on the internet, run with the option `--host 0.0.0.0`
## Option 4: Docker
```sh
-git clone https://github.com/lnbits/lnbits-legend.git
-cd lnbits-legend
-docker build -t lnbits-legend .
+git clone https://github.com/lnbits/lnbits.git
+cd lnbits
+docker build -t lnbits .
cp .env.example .env
mkdir data
docker run --detach --publish 5000:5000 --name lnbits-legend --volume ${PWD}/.env:/app/.env --volume ${PWD}/data/:/app/data lnbits-legend
@@ -135,8 +136,8 @@ You can either run those commands, then `source ~/.bash_profile` or, if you don'
Once installed, run the following commands.
```
-git clone https://github.com/lnbits/lnbits-legend.git
-cd lnbits-legend
+git clone https://github.com/lnbits/lnbits.git
+cd lnbits
fly auth login
[complete login process]
fly launch
@@ -437,8 +438,8 @@ If you want to run LNbits on your Umbrel but want it to be reached through clear
To install using docker you first need to build the docker image as:
```
-git clone https://github.com/lnbits/lnbits-legend.git
-cd lnbits-legend
+git clone https://github.com/lnbits/lnbits.git
+cd lnbits
docker build -t lnbits-legend .
```
diff --git a/lnbits/core/templates/core/index.html b/lnbits/core/templates/core/index.html
index a28030c0..104dccd9 100644
--- a/lnbits/core/templates/core/index.html
+++ b/lnbits/core/templates/core/index.html
@@ -66,7 +66,7 @@
outline
color="grey"
type="a"
- href="https://github.com/lnbits/lnbits-legend"
+ href="https://github.com/lnbits/lnbits"
target="_blank"
rel="noopener"
>View project in GitHub dict:
+ params: dict = {}
if not is_supported_lnurl_subprotocol(tag):
raise LnurlValidationError(f'Unsupported subprotocol: "{tag}"')
if tag == "withdrawRequest":
@@ -85,15 +85,15 @@ def query_to_signing_payload(query: Dict[str, str]) -> str:
payload = []
for key in sorted_keys:
if not key == "signature":
- encoded_key = urllib.parse.quote(key, safe=encode_uri_component_safe_chars)
- encoded_value = urllib.parse.quote(
+ encoded_key = parse.quote(key, safe=encode_uri_component_safe_chars)
+ encoded_value = parse.quote(
query[key], safe=encode_uri_component_safe_chars
)
payload.append(f"{encoded_key}={encoded_value}")
return "&".join(payload)
-unshorten_rules = {
+unshorten_rules: dict[str, dict] = {
"query": {"n": "nonce", "s": "signature", "t": "tag"},
"tags": {
"c": "channelRequest",
@@ -114,7 +114,7 @@ unshorten_rules = {
}
-def unshorten_lnurl_query(query: Dict[str, str]) -> Dict[str, str]:
+def unshorten_lnurl_query(query: dict) -> Dict[str, str]:
new_query = {}
rules = unshorten_rules
if "tag" in query:
@@ -131,9 +131,9 @@ def unshorten_lnurl_query(query: Dict[str, str]) -> Dict[str, str]:
if not tag in rules["params"]:
raise LnurlValidationError(f'Unknown tag: "{tag}"')
for key in query:
- if key in rules["params"][tag]:
+ if key in rules["params"][str(tag)]:
short_param_key = key
- long_param_key = rules["params"][tag][short_param_key]
+ long_param_key = rules["params"][str(tag)][short_param_key]
if short_param_key in query:
new_query[long_param_key] = query[short_param_key]
else:
@@ -146,7 +146,7 @@ def unshorten_lnurl_query(query: Dict[str, str]) -> Dict[str, str]:
if short_key in query:
new_query[long_key] = query[short_key]
else:
- new_query[long_key] = query[long_key]
+ new_query[long_key] = query[str(long_key)]
else:
# Keep unknown key/value pairs unchanged:
new_query[key] = query[key]
diff --git a/lnbits/extensions/bleskomat/lnurl_api.py b/lnbits/extensions/bleskomat/lnurl_api.py
index 33e33a70..bdac5fec 100644
--- a/lnbits/extensions/bleskomat/lnurl_api.py
+++ b/lnbits/extensions/bleskomat/lnurl_api.py
@@ -1,6 +1,5 @@
import json
import math
-import traceback
from http import HTTPStatus
from loguru import logger
@@ -28,7 +27,7 @@ from .helpers import (
@bleskomat_ext.get("/u", name="bleskomat.api_bleskomat_lnurl")
async def api_bleskomat_lnurl(req: Request):
try:
- query = req.query_params
+ query = dict(req.query_params)
# Unshorten query if "s" is used instead of "signature".
if "s" in query:
@@ -89,11 +88,15 @@ async def api_bleskomat_lnurl(req: Request):
# Convert to msats:
params[key] = int(amount_sats_less_fee * 1e3)
except LnurlValidationError as e:
- raise LnurlHttpError(e.message, HTTPStatus.BAD_REQUEST)
+ raise LnurlHttpError(str(e), HTTPStatus.BAD_REQUEST)
# Create a new LNURL using the query parameters provided in the signed URL.
- params = json.JSONEncoder().encode(params)
+ json_params = json.JSONEncoder().encode(params)
lnurl = await create_bleskomat_lnurl(
- bleskomat=bleskomat, secret=secret, tag=tag, params=params, uses=1
+ bleskomat=bleskomat,
+ secret=secret,
+ tag=tag,
+ params=json_params,
+ uses=1,
)
# Reply with LNURL response object.
diff --git a/lnbits/extensions/bleskomat/models.py b/lnbits/extensions/bleskomat/models.py
index 364cbe22..6dc88dc0 100644
--- a/lnbits/extensions/bleskomat/models.py
+++ b/lnbits/extensions/bleskomat/models.py
@@ -2,10 +2,9 @@ import json
import time
from typing import Dict
-from fastapi.params import Query
+from fastapi import Query, Request
from loguru import logger
from pydantic import BaseModel, validator
-from starlette.requests import Request
from lnbits import bolt11
from lnbits.core.services import PaymentFailure, pay_invoice
@@ -80,7 +79,7 @@ class BleskomatLnurl(BaseModel):
response["k1"] = secret
return response
- def validate_action(self, query: Dict[str, str]) -> None:
+ def validate_action(self, query) -> None:
tag = self.tag
params = json.loads(self.params)
# Perform tag-specific checks.
@@ -109,7 +108,7 @@ class BleskomatLnurl(BaseModel):
else:
raise LnurlValidationError(f'Unknown subprotocol: "{tag}"')
- async def execute_action(self, query: Dict[str, str]):
+ async def execute_action(self, query):
self.validate_action(query)
used = False
async with db.connect() as conn:
diff --git a/lnbits/extensions/bleskomat/views.py b/lnbits/extensions/bleskomat/views.py
index 92d47513..370f2ec3 100644
--- a/lnbits/extensions/bleskomat/views.py
+++ b/lnbits/extensions/bleskomat/views.py
@@ -1,5 +1,4 @@
-from fastapi import Request
-from fastapi.params import Depends
+from fastapi import Depends, Request
from fastapi.templating import Jinja2Templates
from starlette.responses import HTMLResponse
diff --git a/lnbits/extensions/bleskomat/views_api.py b/lnbits/extensions/bleskomat/views_api.py
index b6e417bb..3e7573bc 100644
--- a/lnbits/extensions/bleskomat/views_api.py
+++ b/lnbits/extensions/bleskomat/views_api.py
@@ -6,7 +6,6 @@ from starlette.exceptions import HTTPException
from lnbits.core.crud import get_user
from lnbits.decorators import WalletTypeInfo, require_admin_key
-from lnbits.extensions.bleskomat.models import CreateBleskomat
from . import bleskomat_ext
from .crud import (
@@ -17,6 +16,7 @@ from .crud import (
update_bleskomat,
)
from .exchange_rates import fetch_fiat_exchange_rate
+from .models import CreateBleskomat
@bleskomat_ext.get("/api/v1/bleskomats")
@@ -27,7 +27,8 @@ async def api_bleskomats(
wallet_ids = [wallet.wallet.id]
if all_wallets:
- wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
+ user = await get_user(wallet.wallet.user)
+ wallet_ids = user.wallet_ids if user else []
return [bleskomat.dict() for bleskomat in await get_bleskomats(wallet_ids)]
@@ -54,9 +55,9 @@ async def api_bleskomat_create_or_update(
wallet: WalletTypeInfo = Depends(require_admin_key),
bleskomat_id=None,
):
+ fiat_currency = data.fiat_currency
+ exchange_rate_provider = data.exchange_rate_provider
try:
- fiat_currency = data.fiat_currency
- exchange_rate_provider = data.exchange_rate_provider
await fetch_fiat_exchange_rate(
currency=fiat_currency, provider=exchange_rate_provider
)
@@ -79,6 +80,7 @@ async def api_bleskomat_create_or_update(
else:
bleskomat = await create_bleskomat(wallet_id=wallet.wallet.id, data=data)
+ assert bleskomat
return bleskomat.dict()
diff --git a/lnbits/extensions/boltz/templates/boltz/_api_docs.html b/lnbits/extensions/boltz/templates/boltz/_api_docs.html
index 0edc413a..704a8db5 100644
--- a/lnbits/extensions/boltz/templates/boltz/_api_docs.html
+++ b/lnbits/extensions/boltz/templates/boltz/_api_docs.html
@@ -32,7 +32,7 @@
More details
diff --git a/lnbits/extensions/cashu/templates/cashu/mint.html b/lnbits/extensions/cashu/templates/cashu/mint.html
index bcb919ff..a6959ec2 100644
--- a/lnbits/extensions/cashu/templates/cashu/mint.html
+++ b/lnbits/extensions/cashu/templates/cashu/mint.html
@@ -4,18 +4,19 @@
-
- {{ mint_name }}
+
+ {{ mint_name }}
+
Open walletclick to open wallet
@@ -58,24 +59,34 @@
This service is in BETA
- We hold no responsibility for people losing access to funds. Use at
- your own risk!
+ Cashu is still experimental and in active development. There are
+ likely bugs in this implementation so please use this with caution. We
+ hold no responsibility for people losing access to funds. Use at your
+ own risk!
-
- {% endblock %} {% block scripts %}
-
-
-
- {% endblock %}
+
+{% endblock %} {% block scripts %}
+
+
+
+{% endblock %}
diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html
index d075009e..1bf6241f 100644
--- a/lnbits/extensions/cashu/templates/cashu/wallet.html
+++ b/lnbits/extensions/cashu/templates/cashu/wallet.html
@@ -1479,15 +1479,15 @@ page_container %}
},
constructOutputs: async function (amounts, secrets) {
- const blindedMessages = []
+ const outputs = []
const rs = []
for (let i = 0; i < amounts.length; i++) {
const {B_, r} = await step1Alice(secrets[i])
- blindedMessages.push({amount: amounts[i], B_: B_})
+ outputs.push({amount: amounts[i], B_: B_})
rs.push(r)
}
return {
- blindedMessages,
+ outputs,
rs
}
},
@@ -1581,25 +1581,26 @@ page_container %}
mintApi: async function (amounts, payment_hash, verbose = true) {
/*
asks the mint to check whether the invoice with payment_hash has been paid
- and requests signing of the attached outputs (blindedMessages)
+ and requests signing of the attached outputs.
*/
console.log('### promises', payment_hash)
try {
let secrets = await this.generateSecrets(amounts)
- let {blindedMessages, rs} = await this.constructOutputs(
- amounts,
- secrets
- )
+ let {outputs, rs} = await this.constructOutputs(amounts, secrets)
const promises = await LNbits.api.request(
'POST',
`/cashu/api/v1/${this.mintId}/mint?payment_hash=${payment_hash}`,
'',
{
- blinded_messages: blindedMessages
+ outputs
}
)
- console.log('### promises data', promises.data)
- let proofs = await this.constructProofs(promises.data, secrets, rs)
+ console.log('### promises data', promises.data.promises)
+ let proofs = await this.constructProofs(
+ promises.data.promises,
+ secrets,
+ rs
+ )
return proofs
} catch (error) {
console.error(error)
@@ -1682,16 +1683,11 @@ page_container %}
'number of secrets does not match number of outputs.'
)
}
- let {blindedMessages, rs} = await this.constructOutputs(
- amounts,
- secrets
- )
+ let {outputs, rs} = await this.constructOutputs(amounts, secrets)
const payload = {
amount,
proofs,
- outputs: {
- blinded_messages: blindedMessages
- }
+ outputs
}
console.log('payload', JSON.stringify(payload))
@@ -1881,10 +1877,7 @@ page_container %}
'amount with fees',
amount
)
- // if (amount > balance()) {
- // LNbits.utils.notifyApiError('Balance too low')
- // return
- // }
+
let {fristProofs, scndProofs} = await this.splitToSend(
this.proofs,
amount
@@ -2132,6 +2125,19 @@ page_container %}
return paid
},
+ findTokenForAmount: function (amount) {
+ for (const token of this.proofs) {
+ const index = token.promises?.findIndex(p => p.amount === amount)
+ if (index >= 0) {
+ return {
+ promise: token.promises[index],
+ secret: token.secrets[index],
+ r: token.rs[index]
+ }
+ }
+ }
+ },
+
////////////// WORKERS //////////////
clearAllWorkers: function () {
@@ -2220,76 +2226,6 @@ page_container %}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- findTokenForAmount: function (amount) {
- for (const token of this.proofs) {
- const index = token.promises?.findIndex(p => p.amount === amount)
- if (index >= 0) {
- return {
- promise: token.promises[index],
- secret: token.secrets[index],
- r: token.rs[index]
- }
- }
- }
- },
-
- // checkInvoice: function () {
- // console.log('#### checkInvoice')
- // try {
- // const invoice = decode(this.payInvoiceData.data.request)
-
- // const cleanInvoice = {
- // msat: invoice.human_readable_part.amount,
- // sat: invoice.human_readable_part.amount / 1000,
- // fsat: LNbits.utils.formatSat(
- // invoice.human_readable_part.amount / 1000
- // )
- // }
-
- // _.each(invoice.data.tags, tag => {
- // if (_.isObject(tag) && _.has(tag, 'description')) {
- // if (tag.description === 'payment_hash') {
- // cleanInvoice.hash = tag.value
- // } else if (tag.description === 'description') {
- // cleanInvoice.description = tag.value
- // } else if (tag.description === 'expiry') {
- // var expireDate = new Date(
- // (invoice.data.time_stamp + tag.value) * 1000
- // )
- // cleanInvoice.expireDate = Quasar.utils.date.formatDate(
- // expireDate,
- // 'YYYY-MM-DDTHH:mm:ss.SSSZ'
- // )
- // cleanInvoice.expired = false // TODO
- // }
- // }
-
- // this.payInvoiceData.invoice = cleanInvoice
- // })
-
- // console.log(
- // '#### this.payInvoiceData.invoice',
- // this.payInvoiceData.invoice
- // )
- // } catch (error) {
- // this.$q.notify({
- // timeout: 5000,
- // type: 'warning',
- // message: 'Could not decode invoice',
- // caption: error + '',
- // position: 'top',
- // actions: [
- // {
- // icon: 'close',
- // color: 'white',
- // handler: () => {}
- // }
- // ]
- // })
- // throw error
- // }
- // },
-
////////////// STORAGE /////////////
getLocalstorageToFile: async function () {
diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py
index b66bbdc1..2017df00 100644
--- a/lnbits/extensions/cashu/views_api.py
+++ b/lnbits/extensions/cashu/views_api.py
@@ -12,7 +12,8 @@ from cashu.core.base import (
GetMintResponse,
Invoice,
MeltRequest,
- MintRequest,
+ PostMintRequest,
+ PostMintResponse,
PostSplitResponse,
SplitRequest,
)
@@ -204,10 +205,10 @@ async def request_mint(cashu_id: str = Query(None), amount: int = 0) -> GetMintR
@cashu_ext.post("/api/v1/{cashu_id}/mint")
async def mint(
- data: MintRequest,
+ data: PostMintRequest,
cashu_id: str = Query(None),
payment_hash: str = Query(None),
-) -> List[BlindedSignature]:
+) -> PostMintResponse:
"""
Requests the minting of tokens belonging to a paid payment request.
Call this endpoint after `GET /mint`.
@@ -245,7 +246,7 @@ async def mint(
)
try:
- total_requested = sum([bm.amount for bm in data.blinded_messages])
+ total_requested = sum([bm.amount for bm in data.outputs])
if total_requested > invoice.amount:
raise HTTPException(
status_code=HTTPStatus.PAYMENT_REQUIRED,
@@ -257,10 +258,8 @@ async def mint(
status_code=HTTPStatus.PAYMENT_REQUIRED, detail="Invoice not paid."
)
- promises = await ledger._generate_promises(
- B_s=data.blinded_messages, keyset=keyset
- )
- return promises
+ promises = await ledger._generate_promises(B_s=data.outputs, keyset=keyset)
+ return PostMintResponse(promises=promises)
except (Exception, HTTPException) as e:
logger.debug(f"Cashu: /melt {str(e) or getattr(e, 'detail')}")
# unset issued flag because something went wrong
@@ -274,10 +273,8 @@ async def mint(
)
else:
# only used for testing when LIGHTNING=false
- promises = await ledger._generate_promises(
- B_s=data.blinded_messages, keyset=keyset
- )
- return promises
+ promises = await ledger._generate_promises(B_s=data.outputs, keyset=keyset)
+ return PostMintResponse(promises=promises)
@cashu_ext.post("/api/v1/{cashu_id}/melt")
@@ -421,7 +418,7 @@ async def split(
)
amount = payload.amount
- outputs = payload.outputs.blinded_messages
+ outputs = payload.outputs
assert outputs, Exception("no outputs provided.")
split_return = None
try:
diff --git a/lnbits/extensions/lnaddress/templates/lnaddress/_api_docs.html b/lnbits/extensions/lnaddress/templates/lnaddress/_api_docs.html
index 300f56e9..f4d1f651 100644
--- a/lnbits/extensions/lnaddress/templates/lnaddress/_api_docs.html
+++ b/lnbits/extensions/lnaddress/templates/lnaddress/_api_docs.html
@@ -14,7 +14,7 @@
More details
diff --git a/lnbits/extensions/lnaddress/templates/lnaddress/index.html b/lnbits/extensions/lnaddress/templates/lnaddress/index.html
index 55d900f4..41602581 100644
--- a/lnbits/extensions/lnaddress/templates/lnaddress/index.html
+++ b/lnbits/extensions/lnaddress/templates/lnaddress/index.html
@@ -195,7 +195,7 @@
Check extension
documentation!
diff --git a/lnbits/extensions/nostrnip5/crud.py b/lnbits/extensions/nostrnip5/crud.py
index fe71b981..66b9840b 100644
--- a/lnbits/extensions/nostrnip5/crud.py
+++ b/lnbits/extensions/nostrnip5/crud.py
@@ -3,7 +3,7 @@ from typing import List, Optional, Union
from lnbits.helpers import urlsafe_short_hash
from . import db
-from .models import Address, CreateAddressData, CreateDomainData, Domain
+from .models import Address, CreateAddressData, CreateDomainData, Domain, EditDomainData
async def get_domain(domain_id: str) -> Optional[Domain]:
@@ -170,6 +170,26 @@ async def create_address_internal(domain_id: str, data: CreateAddressData) -> Ad
return address
+async def update_domain_internal(wallet_id: str, data: EditDomainData) -> Domain:
+ if data.currency != "Satoshis":
+ amount = data.amount * 100
+ else:
+ amount = data.amount
+ print(data)
+ await db.execute(
+ """
+ UPDATE nostrnip5.domains
+ SET amount = ?, currency = ?
+ WHERE id = ?
+ """,
+ (int(amount), data.currency, data.id),
+ )
+
+ domain = await get_domain(data.id)
+ assert domain, "Domain couldn't be updated"
+ return domain
+
+
async def create_domain_internal(wallet_id: str, data: CreateDomainData) -> Domain:
domain_id = urlsafe_short_hash()
diff --git a/lnbits/extensions/nostrnip5/models.py b/lnbits/extensions/nostrnip5/models.py
index e02f2909..5abbf128 100644
--- a/lnbits/extensions/nostrnip5/models.py
+++ b/lnbits/extensions/nostrnip5/models.py
@@ -24,6 +24,16 @@ class CreateDomainData(BaseModel):
domain: str
+class EditDomainData(BaseModel):
+ id: str
+ currency: str
+ amount: float = Query(..., ge=0.01)
+
+ @classmethod
+ def from_row(cls, row: Row) -> "EditDomainData":
+ return cls(**dict(row))
+
+
class Domain(BaseModel):
id: str
wallet: str
diff --git a/lnbits/extensions/nostrnip5/templates/nostrnip5/index.html b/lnbits/extensions/nostrnip5/templates/nostrnip5/index.html
index 6b805ccc..8ebaa502 100644
--- a/lnbits/extensions/nostrnip5/templates/nostrnip5/index.html
+++ b/lnbits/extensions/nostrnip5/templates/nostrnip5/index.html
@@ -73,6 +73,14 @@
:color="($q.dark.isActive) ? 'grey-7' : 'grey-5'"
@click="deleteDomain(props.row.id)"
>
+
{{ col.value }}
@@ -226,6 +234,39 @@
+
+
+
+
+
+
+
+ Update Amount
+ Cancel
+
+
+
+
+
item.id === itemId)
+ if (item.image.startsWith('data:')) {
+ this.itemDialog.urlImg = false
+ }
this.itemDialog.data = item
},
imageAdded(file) {
@@ -48,17 +61,12 @@ new Vue({
let image = new Image()
image.src = blobURL
image.onload = async () => {
+ let fit = imgSizeFit(image, 100, 100)
let canvas = document.createElement('canvas')
- canvas.setAttribute('width', 100)
- canvas.setAttribute('height', 100)
- await pica.resize(image, canvas, {
- quality: 0,
- alpha: true,
- unsharpAmount: 95,
- unsharpRadius: 0.9,
- unsharpThreshold: 70
- })
- this.itemDialog.data.image = canvas.toDataURL()
+ canvas.setAttribute('width', fit.width)
+ canvas.setAttribute('height', fit.height)
+ output = await pica.resize(image, canvas)
+ this.itemDialog.data.image = output.toDataURL('image/jpeg', 0.4)
this.itemDialog = {...this.itemDialog}
}
},
@@ -155,6 +163,7 @@ new Vue({
this.loadShop()
this.itemDialog.show = false
+ this.itemDialog.urlImg = true
this.itemDialog.data = {...defaultItemData}
},
toggleItem(itemId) {
diff --git a/lnbits/extensions/offlineshop/templates/offlineshop/index.html b/lnbits/extensions/offlineshop/templates/offlineshop/index.html
index 61f6093e..80a7bbb8 100644
--- a/lnbits/extensions/offlineshop/templates/offlineshop/index.html
+++ b/lnbits/extensions/offlineshop/templates/offlineshop/index.html
@@ -237,7 +237,7 @@
@@ -266,7 +266,16 @@
type="text"
label="Brief description"
>
+
+
100:
+ raise HTTPException(
+ status_code=HTTPStatus.BAD_REQUEST,
+ detail=f"Image size is too big, {int(image_size)}Kb. Max: 100kb, Compress the image at https://tinypng.com, or use an URL.",
+ )
if data.unit != "sat":
data.price = data.price * 100
if item_id == None:
diff --git a/lnbits/extensions/smtp/crud.py b/lnbits/extensions/smtp/crud.py
index 2eee4c3d..c7b96df5 100644
--- a/lnbits/extensions/smtp/crud.py
+++ b/lnbits/extensions/smtp/crud.py
@@ -1,10 +1,9 @@
-from http import HTTPStatus
from typing import List, Optional, Union
from lnbits.helpers import urlsafe_short_hash
from . import db
-from .models import CreateEmail, CreateEmailaddress, Emailaddresses, Emails
+from .models import CreateEmail, CreateEmailaddress, Email, Emailaddress
from .smtp import send_mail
@@ -17,7 +16,7 @@ def get_test_mail(email, testemail):
)
-async def create_emailaddress(data: CreateEmailaddress) -> Emailaddresses:
+async def create_emailaddress(data: CreateEmailaddress) -> Emailaddress:
emailaddress_id = urlsafe_short_hash()
@@ -50,7 +49,7 @@ async def create_emailaddress(data: CreateEmailaddress) -> Emailaddresses:
return new_emailaddress
-async def update_emailaddress(emailaddress_id: str, **kwargs) -> Emailaddresses:
+async def update_emailaddress(emailaddress_id: str, **kwargs) -> Emailaddress:
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
await db.execute(
f"UPDATE smtp.emailaddress SET {q} WHERE id = ?",
@@ -65,30 +64,22 @@ async def update_emailaddress(emailaddress_id: str, **kwargs) -> Emailaddresses:
await send_mail(row, email)
assert row, "Newly updated emailaddress couldn't be retrieved"
- return Emailaddresses(**row)
+ return Emailaddress(**row)
-async def get_emailaddress(emailaddress_id: str) -> Optional[Emailaddresses]:
+async def get_emailaddress(emailaddress_id: str) -> Optional[Emailaddress]:
row = await db.fetchone(
"SELECT * FROM smtp.emailaddress WHERE id = ?", (emailaddress_id,)
)
- return Emailaddresses(**row) if row else None
+ return Emailaddress(**row) if row else None
-async def get_emailaddress_by_email(email: str) -> Optional[Emailaddresses]:
+async def get_emailaddress_by_email(email: str) -> Optional[Emailaddress]:
row = await db.fetchone("SELECT * FROM smtp.emailaddress WHERE email = ?", (email,))
- return Emailaddresses(**row) if row else None
+ return Emailaddress(**row) if row else None
-# async def get_emailAddressByEmail(email: str) -> Optional[Emails]:
-# row = await db.fetchone(
-# "SELECT s.*, d.emailaddress as emailaddress FROM smtp.email s INNER JOIN smtp.emailaddress d ON (s.emailaddress_id = d.id) WHERE s.emailaddress = ?",
-# (email,),
-# )
-# return Subdomains(**row) if row else None
-
-
-async def get_emailaddresses(wallet_ids: Union[str, List[str]]) -> List[Emailaddresses]:
+async def get_emailaddresses(wallet_ids: Union[str, List[str]]) -> List[Emailaddress]:
if isinstance(wallet_ids, str):
wallet_ids = [wallet_ids]
@@ -97,21 +88,22 @@ async def get_emailaddresses(wallet_ids: Union[str, List[str]]) -> List[Emailadd
f"SELECT * FROM smtp.emailaddress WHERE wallet IN ({q})", (*wallet_ids,)
)
- return [Emailaddresses(**row) for row in rows]
+ return [Emailaddress(**row) for row in rows]
async def delete_emailaddress(emailaddress_id: str) -> None:
await db.execute("DELETE FROM smtp.emailaddress WHERE id = ?", (emailaddress_id,))
-## create emails
-async def create_email(payment_hash, wallet, data: CreateEmail) -> Emails:
+async def create_email(wallet: str, data: CreateEmail, payment_hash: str = "") -> Email:
+ id = urlsafe_short_hash()
await db.execute(
"""
- INSERT INTO smtp.email (id, wallet, emailaddress_id, subject, receiver, message, paid)
- VALUES (?, ?, ?, ?, ?, ?, ?)
+ INSERT INTO smtp.email (id, payment_hash, wallet, emailaddress_id, subject, receiver, message, paid)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
""",
(
+ id,
payment_hash,
wallet,
data.emailaddress_id,
@@ -122,36 +114,34 @@ async def create_email(payment_hash, wallet, data: CreateEmail) -> Emails:
),
)
- new_email = await get_email(payment_hash)
+ new_email = await get_email(id)
assert new_email, "Newly created email couldn't be retrieved"
return new_email
-async def set_email_paid(payment_hash: str) -> Emails:
- email = await get_email(payment_hash)
+async def set_email_paid(payment_hash: str) -> bool:
+ email = await get_email_by_payment_hash(payment_hash)
if email and email.paid == False:
await db.execute(
- """
- UPDATE smtp.email
- SET paid = true
- WHERE id = ?
- """,
- (payment_hash,),
+ f"UPDATE smtp.email SET paid = true WHERE payment_hash = ?", (payment_hash,)
)
- new_email = await get_email(payment_hash)
- assert new_email, "Newly paid email couldn't be retrieved"
- return new_email
+ return True
+ return False
-async def get_email(email_id: str) -> Optional[Emails]:
+async def get_email_by_payment_hash(payment_hash: str) -> Optional[Email]:
row = await db.fetchone(
- "SELECT s.*, d.email as emailaddress FROM smtp.email s INNER JOIN smtp.emailaddress d ON (s.emailaddress_id = d.id) WHERE s.id = ?",
- (email_id,),
+ f"SELECT * FROM smtp.email WHERE payment_hash = ?", (payment_hash,)
)
- return Emails(**row) if row else None
+ return Email(**row) if row else None
-async def get_emails(wallet_ids: Union[str, List[str]]) -> List[Emails]:
+async def get_email(id: str) -> Optional[Email]:
+ row = await db.fetchone(f"SELECT * FROM smtp.email WHERE id = ?", (id,))
+ return Email(**row) if row else None
+
+
+async def get_emails(wallet_ids: Union[str, List[str]]) -> List[Email]:
if isinstance(wallet_ids, str):
wallet_ids = [wallet_ids]
@@ -161,7 +151,7 @@ async def get_emails(wallet_ids: Union[str, List[str]]) -> List[Emails]:
(*wallet_ids,),
)
- return [Emails(**row) for row in rows]
+ return [Email(**row) for row in rows]
async def delete_email(email_id: str) -> None:
diff --git a/lnbits/extensions/smtp/migrations.py b/lnbits/extensions/smtp/migrations.py
index 16d50166..fbe77d73 100644
--- a/lnbits/extensions/smtp/migrations.py
+++ b/lnbits/extensions/smtp/migrations.py
@@ -33,3 +33,7 @@ async def m001_initial(db):
);
"""
)
+
+
+async def m002_add_payment_hash(db):
+ await db.execute(f"ALTER TABLE smtp.email ADD COLUMN payment_hash TEXT;")
diff --git a/lnbits/extensions/smtp/models.py b/lnbits/extensions/smtp/models.py
index e2f3fc13..bb0e1f2c 100644
--- a/lnbits/extensions/smtp/models.py
+++ b/lnbits/extensions/smtp/models.py
@@ -15,7 +15,7 @@ class CreateEmailaddress(BaseModel):
cost: int = Query(..., ge=0)
-class Emailaddresses(BaseModel):
+class Emailaddress(BaseModel):
id: str
wallet: str
email: str
@@ -36,7 +36,7 @@ class CreateEmail(BaseModel):
message: str = Query(...)
-class Emails(BaseModel):
+class Email(BaseModel):
id: str
wallet: str
emailaddress_id: str
diff --git a/lnbits/extensions/smtp/smtp.py b/lnbits/extensions/smtp/smtp.py
index e77bc0fa..43253b54 100644
--- a/lnbits/extensions/smtp/smtp.py
+++ b/lnbits/extensions/smtp/smtp.py
@@ -4,83 +4,107 @@ import time
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.utils import formatdate
-from http import HTTPStatus
from smtplib import SMTP_SSL as SMTP
+from typing import Union
from loguru import logger
-from starlette.exceptions import HTTPException
+
+from .models import CreateEmail, CreateEmailaddress, Email, Emailaddress
+
+
+async def send_mail(
+ emailaddress: Union[Emailaddress, CreateEmailaddress],
+ email: Union[Email, CreateEmail],
+):
+ smtp_client = SmtpService(emailaddress)
+ message = smtp_client.create_message(email)
+ await smtp_client.send_mail(email.receiver, message)
def valid_email(s):
# https://regexr.com/2rhq7
- pat = "[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?"
+ pat = r"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?"
if re.match(pat, s):
return True
- msg = f"SMTP - invalid email: {s}."
- logger.error(msg)
- raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=msg)
+ log = f"SMTP - invalid email: {s}."
+ logger.error(log)
+ raise Exception(log)
-async def send_mail(emailaddress, email):
- valid_email(emailaddress.email)
- valid_email(email.receiver)
+class SmtpService:
+ def __init__(self, emailaddress: Union[Emailaddress, CreateEmailaddress]) -> None:
+ self.sender = emailaddress.email
+ self.smtp_server = emailaddress.smtp_server
+ self.smtp_port = emailaddress.smtp_port
+ self.smtp_user = emailaddress.smtp_user
+ self.smtp_password = emailaddress.smtp_password
- ts = time.time()
- date = formatdate(ts, True)
-
- msg = MIMEMultipart("alternative")
- msg = MIMEMultipart("alternative")
- msg["Date"] = date
- msg["Subject"] = email.subject
- msg["From"] = emailaddress.email
- msg["To"] = email.receiver
-
- signature = "Email sent anonymiously by LNbits Sendmail extension."
- text = f"""
-{email.message}
-
-{signature}
-"""
-
- html = f"""
-
-
-
- {email.message}
-
-
{signature}
-
-
-"""
-
- part1 = MIMEText(text, "plain")
- part2 = MIMEText(html, "html")
- msg.attach(part1)
- msg.attach(part2)
-
- try:
- conn = SMTP(
- host=emailaddress.smtp_server, port=emailaddress.smtp_port, timeout=10
+ def render_email(self, email: Union[Email, CreateEmail]):
+ signature: str = "Email sent by LNbits SMTP extension."
+ text = f"{email.message}\n\n{signature}"
+ html = (
+ """
+
+
+
+ """
+ + email.message
+ + """
+ """
+ + signature
+ + """
+
+
+ """
)
- logger.debug("SMTP - connected to smtp server.")
- # conn.set_debuglevel(True)
- except:
- msg = f"SMTP - error connecting to smtp server: {emailaddress.smtp_server}:{emailaddress.smtp_port}."
- logger.error(msg)
- raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=msg)
- try:
- conn.login(emailaddress.smtp_user, emailaddress.smtp_password)
- logger.debug("SMTP - successful login to smtp server.")
- except:
- msg = f"SMTP - error login into smtp {emailaddress.smtp_user}."
- logger.error(msg)
- raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=msg)
- try:
- conn.sendmail(emailaddress.email, email.receiver, msg.as_string())
- logger.debug("SMTP - successfully send email.")
- except socket.error as e:
- msg = f"SMTP - error sending email: {str(e)}."
- logger.error(msg)
- raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=msg)
- finally:
- conn.quit()
+ return text, html
+
+ def create_message(self, email: Union[Email, CreateEmail]):
+ ts = time.time()
+ date = formatdate(ts, True)
+
+ msg = MIMEMultipart("alternative")
+ msg["Date"] = date
+ msg["Subject"] = email.subject
+ msg["From"] = self.sender
+ msg["To"] = email.receiver
+
+ text, html = self.render_email(email)
+
+ part1 = MIMEText(text, "plain")
+ part2 = MIMEText(html, "html")
+ msg.attach(part1)
+ msg.attach(part2)
+ return msg
+
+ async def send_mail(self, receiver, msg: MIMEMultipart):
+
+ valid_email(self.sender)
+ valid_email(receiver)
+
+ try:
+ conn = SMTP(host=self.smtp_server, port=int(self.smtp_port), timeout=10)
+ logger.debug("SMTP - connected to smtp server.")
+ # conn.set_debuglevel(True)
+ except:
+ log = f"SMTP - error connecting to smtp server: {self.smtp_server}:{self.smtp_port}."
+ logger.debug(log)
+ raise Exception(log)
+
+ try:
+ conn.login(self.smtp_user, self.smtp_password)
+ logger.debug("SMTP - successful login to smtp server.")
+ except:
+ log = f"SMTP - error login into smtp {self.smtp_user}."
+ logger.error(log)
+ raise Exception(log)
+
+ try:
+ conn.sendmail(self.sender, receiver, msg.as_string())
+ logger.debug("SMTP - successfully send email.")
+ except socket.error as e:
+ log = f"SMTP - error sending email: {str(e)}."
+ logger.error(log)
+ raise Exception(log)
+ finally:
+ conn.quit()
diff --git a/lnbits/extensions/smtp/tasks.py b/lnbits/extensions/smtp/tasks.py
index 9c544473..93ed33ba 100644
--- a/lnbits/extensions/smtp/tasks.py
+++ b/lnbits/extensions/smtp/tasks.py
@@ -5,7 +5,7 @@ from loguru import logger
from lnbits.core.models import Payment
from lnbits.tasks import register_invoice_listener
-from .crud import get_email, get_emailaddress, set_email_paid
+from .crud import get_email_by_payment_hash, get_emailaddress, set_email_paid
from .smtp import send_mail
@@ -21,7 +21,7 @@ async def on_invoice_paid(payment: Payment) -> None:
if payment.extra.get("tag") != "smtp":
return
- email = await get_email(payment.checking_id)
+ email = await get_email_by_payment_hash(payment.checking_id)
if not email:
logger.error("SMTP: email can not by fetched")
return
diff --git a/lnbits/extensions/smtp/templates/smtp/index.html b/lnbits/extensions/smtp/templates/smtp/index.html
index bf43ad7f..c64cdcfa 100644
--- a/lnbits/extensions/smtp/templates/smtp/index.html
+++ b/lnbits/extensions/smtp/templates/smtp/index.html
@@ -57,6 +57,14 @@
:href="props.row.displayUrl"
target="_blank"
>
+
{{ col.value }}
@@ -154,6 +162,42 @@
+
+
+
+
+
+
+
+ Submit
+
+
+
+
@@ -316,10 +360,10 @@
emailsTable: {
columns: [
{
- name: 'emailaddress',
+ name: 'emailaddress_id',
align: 'left',
label: 'From',
- field: 'emailaddress'
+ field: 'emailaddress_id'
},
{
name: 'receiver',
@@ -350,6 +394,10 @@
rowsPerPage: 10
}
},
+ emailDialog: {
+ show: false,
+ data: {}
+ },
emailaddressDialog: {
show: false,
data: {}
@@ -453,6 +501,33 @@
LNbits.utils.notifyApiError(error)
})
},
+ sendEmail: function () {
+ var self = this
+ var emailaddress = _.findWhere(this.emailaddresses, {
+ id: self.emailDialog.data.emailaddress_id
+ })
+ var wallet = _.findWhere(this.g.user.wallets, {
+ id: emailaddress.wallet
+ })
+ LNbits.api
+ .request(
+ 'POST',
+ '/smtp/api/v1/email/' + emailaddress.id + '/send',
+ wallet.adminkey,
+ self.emailDialog.data
+ )
+ .then(function (response) {
+ self.emailDialog.show = false
+ self.emailDialog.data = {}
+ })
+ .catch(function (error) {
+ LNbits.utils.notifyApiError(error)
+ })
+ },
+ showEmailDialog: function (emailaddress_id) {
+ this.emailDialog.data.emailaddress_id = emailaddress_id
+ this.emailDialog.show = true
+ },
updateEmailaddressDialog: function (formId) {
var link = _.findWhere(this.emailaddresses, {id: formId})
this.emailaddressDialog.data = _.clone(link)
diff --git a/lnbits/extensions/smtp/views_api.py b/lnbits/extensions/smtp/views_api.py
index 4ae1f966..66bc4983 100644
--- a/lnbits/extensions/smtp/views_api.py
+++ b/lnbits/extensions/smtp/views_api.py
@@ -4,7 +4,7 @@ from fastapi import Depends, HTTPException, Query
from lnbits.core.crud import get_user
from lnbits.core.services import check_transaction_status, create_invoice
-from lnbits.decorators import WalletTypeInfo, get_key_type
+from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
from . import smtp_ext
from .crud import (
@@ -13,13 +13,14 @@ from .crud import (
delete_email,
delete_emailaddress,
get_email,
+ get_email_by_payment_hash,
get_emailaddress,
get_emailaddresses,
get_emails,
update_emailaddress,
)
from .models import CreateEmail, CreateEmailaddress
-from .smtp import valid_email
+from .smtp import send_mail, valid_email
## EMAILS
@@ -37,13 +38,14 @@ async def api_email(
@smtp_ext.get("/api/v1/email/{payment_hash}")
async def api_smtp_send_email(payment_hash):
- email = await get_email(payment_hash)
+ email = await get_email_by_payment_hash(payment_hash)
if not email:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST, detail="paymenthash is wrong"
)
emailaddress = await get_emailaddress(email.emailaddress_id)
+ assert emailaddress
try:
status = await check_transaction_status(email.wallet, payment_hash)
@@ -59,11 +61,9 @@ async def api_smtp_send_email(payment_hash):
@smtp_ext.post("/api/v1/email/{emailaddress_id}")
async def api_smtp_make_email(emailaddress_id, data: CreateEmail):
-
valid_email(data.receiver)
emailaddress = await get_emailaddress(emailaddress_id)
- # If the request is coming for the non-existant emailaddress
if not emailaddress:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
@@ -94,6 +94,26 @@ async def api_smtp_make_email(emailaddress_id, data: CreateEmail):
return {"payment_hash": payment_hash, "payment_request": payment_request}
+@smtp_ext.post(
+ "/api/v1/email/{emailaddress_id}/send", dependencies=[Depends(require_admin_key)]
+)
+async def api_smtp_make_email_send(emailaddress_id, data: CreateEmail):
+ valid_email(data.receiver)
+ emailaddress = await get_emailaddress(emailaddress_id)
+ if not emailaddress:
+ raise HTTPException(
+ status_code=HTTPStatus.BAD_REQUEST,
+ detail="Emailaddress address does not exist.",
+ )
+ email = await create_email(wallet=emailaddress.wallet, data=data)
+ if not email:
+ raise HTTPException(
+ status_code=HTTPStatus.NOT_FOUND, detail="Email could not be fetched."
+ )
+ await send_mail(emailaddress, email)
+ return {"sent": True}
+
+
@smtp_ext.delete("/api/v1/email/{email_id}")
async def api_email_delete(email_id, g: WalletTypeInfo = Depends(get_key_type)):
email = await get_email(email_id)
diff --git a/lnbits/extensions/subdomains/templates/subdomains/_api_docs.html b/lnbits/extensions/subdomains/templates/subdomains/_api_docs.html
index 08a2806e..035d67a6 100644
--- a/lnbits/extensions/subdomains/templates/subdomains/_api_docs.html
+++ b/lnbits/extensions/subdomains/templates/subdomains/_api_docs.html
@@ -14,7 +14,7 @@
More details
diff --git a/lnbits/wallets/eclair.py b/lnbits/wallets/eclair.py
index 57eec582..b0c00db2 100644
--- a/lnbits/wallets/eclair.py
+++ b/lnbits/wallets/eclair.py
@@ -8,7 +8,7 @@ from typing import AsyncGenerator, Dict, Optional
import httpx
from loguru import logger
-# TODO: https://github.com/lnbits/lnbits-legend/issues/764
+# TODO: https://github.com/lnbits/lnbits/issues/764
# mypy https://github.com/aaugustin/websockets/issues/940
from websockets import connect # type: ignore
from websockets.exceptions import (
diff --git a/poetry.lock b/poetry.lock
index 698d700d..5a99e198 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -81,21 +81,22 @@ typing-extensions = {version = ">=3.6.5", markers = "python_version < \"3.8\""}
[[package]]
name = "attrs"
-version = "22.1.0"
+version = "22.2.0"
description = "Classes Without Boilerplate"
category = "main"
optional = false
-python-versions = ">=3.5"
+python-versions = ">=3.6"
files = [
- {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"},
- {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"},
+ {file = "attrs-22.2.0-py3-none-any.whl", hash = "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836"},
+ {file = "attrs-22.2.0.tar.gz", hash = "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99"},
]
[package.extras]
-dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"]
-docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"]
-tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"]
-tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"]
+cov = ["attrs[tests]", "coverage-enable-subprocess", "coverage[toml] (>=5.3)"]
+dev = ["attrs[docs,tests]"]
+docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope.interface"]
+tests = ["attrs[tests-no-zope]", "zope.interface"]
+tests-no-zope = ["cloudpickle", "cloudpickle", "hypothesis", "hypothesis", "mypy (>=0.971,<0.990)", "mypy (>=0.971,<0.990)", "pympler", "pympler", "pytest (>=4.3.0)", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-mypy-plugins", "pytest-xdist[psutil]", "pytest-xdist[psutil]"]
[[package]]
name = "base58"
@@ -176,61 +177,65 @@ uvloop = ["uvloop (>=0.15.2)"]
[[package]]
name = "cashu"
-version = "0.6.0"
-description = "Ecash wallet and mint with Bitcoin Lightning support"
+version = "0.8.2"
+description = "Ecash wallet and mint for Bitcoin Lightning"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
- {file = "cashu-0.6.0-py3-none-any.whl", hash = "sha256:54096af145643aab45943b235f95a3357b0ec697835c1411e66523049ffb81f6"},
- {file = "cashu-0.6.0.tar.gz", hash = "sha256:503a90c4ca8d25d0b2c3f78a11b163c32902a726ea5b58e5337dc00eca8e96ad"},
+ {file = "cashu-0.8.2-py3-none-any.whl", hash = "sha256:53893911763c424255bc7112aaba356e0a265e850d770338c70b2d282f67bf99"},
+ {file = "cashu-0.8.2.tar.gz", hash = "sha256:4cdd34a50d14960d2dcc6f5b6120f462688090dd084387860f7a59d16aa102ff"},
]
[package.dependencies]
anyio = {version = "3.6.2", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-attrs = {version = "22.1.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+attrs = {version = "22.2.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
bech32 = {version = "1.2.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
bitstring = {version = "3.1.9", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-certifi = {version = "2022.9.24", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+certifi = {version = "2022.12.7", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
cffi = {version = "1.15.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
charset-normalizer = {version = "2.0.12", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
click = {version = "8.0.4", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-colorama = {version = "0.4.5", markers = "python_version >= \"3.7\" and python_version < \"4.0\" and platform_system == \"Windows\" or python_version >= \"3.7\" and python_version < \"4.0\" and sys_platform == \"win32\""}
+colorama = {version = "0.4.6", markers = "python_version >= \"3.7\" and python_version < \"4.0\" and platform_system == \"Windows\" or python_version >= \"3.7\" and python_version < \"4.0\" and sys_platform == \"win32\""}
+cryptography = {version = "36.0.2", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
ecdsa = {version = "0.18.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
environs = {version = "9.5.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+exceptiongroup = {version = "1.1.0", markers = "python_version >= \"3.7\" and python_version < \"3.11\""}
fastapi = {version = "0.83.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
h11 = {version = "0.12.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
idna = {version = "3.4", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-importlib-metadata = {version = "5.0.0", markers = "python_version >= \"3.7\" and python_version < \"3.8\""}
-iniconfig = {version = "1.1.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+importlib-metadata = {version = "5.2.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+iniconfig = {version = "2.0.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
loguru = {version = "0.6.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-marshmallow = {version = "3.18.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+marshmallow = {version = "3.19.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
outcome = {version = "1.2.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-packaging = {version = "21.3", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+packaging = {version = "23.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
pluggy = {version = "1.0.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-py = {version = "1.11.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
pycparser = {version = "2.21", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-pydantic = {version = "1.10.2", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-pyparsing = {version = "3.0.9", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+pycryptodomex = {version = "3.16.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+pydantic = {version = "1.10.4", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
pysocks = {version = "1.7.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-pytest = {version = "7.1.3", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+pytest = {version = "7.2.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
pytest-asyncio = {version = "0.19.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
python-bitcoinlib = {version = "0.11.2", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
python-dotenv = {version = "0.21.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
represent = {version = "1.6.0.post0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
requests = {version = "2.27.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
secp256k1 = {version = "0.14.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+setuptools = {version = "65.7.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
six = {version = "1.16.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
sniffio = {version = "1.3.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
sqlalchemy = {version = "1.3.24", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
sqlalchemy-aio = {version = "0.17.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
starlette = {version = "0.19.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-tomli = {version = "2.0.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+tomli = {version = "2.0.1", markers = "python_version >= \"3.7\" and python_version < \"3.11\""}
typing-extensions = {version = "4.4.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
-urllib3 = {version = "1.26.12", markers = "python_version >= \"3.7\" and python_version < \"4\""}
+urllib3 = {version = "1.26.14", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
uvicorn = {version = "0.18.3", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+websocket-client = {version = "1.3.3", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
+wheel = {version = "0.38.4", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
win32-setctime = {version = "1.1.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\" and sys_platform == \"win32\""}
-zipp = {version = "3.9.0", markers = "python_version >= \"3.7\" and python_version < \"3.8\""}
+zipp = {version = "3.11.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
[[package]]
name = "cerberus"
@@ -248,14 +253,14 @@ setuptools = "*"
[[package]]
name = "certifi"
-version = "2022.9.24"
+version = "2022.12.7"
description = "Python package for providing Mozilla's CA Bundle."
category = "main"
optional = false
python-versions = ">=3.6"
files = [
- {file = "certifi-2022.9.24-py3-none-any.whl", hash = "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382"},
- {file = "certifi-2022.9.24.tar.gz", hash = "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14"},
+ {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"},
+ {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"},
]
[[package]]
@@ -416,14 +421,14 @@ cffi = ">=1.3.0"
[[package]]
name = "colorama"
-version = "0.4.5"
+version = "0.4.6"
description = "Cross-platform colored terminal text."
category = "main"
optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
files = [
- {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"},
- {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"},
+ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
+ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
[[package]]
@@ -527,10 +532,10 @@ files = [
cffi = ">=1.12"
[package.extras]
-docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx_rtd_theme"]
+docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"]
docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"]
pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"]
-sdist = ["setuptools_rust (>=0.11.4)"]
+sdist = ["setuptools-rust (>=0.11.4)"]
ssh = ["bcrypt (>=3.1.5)"]
test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0)", "pytest-cov", "pytest-subtests", "pytest-xdist", "pytz"]
@@ -599,6 +604,21 @@ 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.1.0"
+description = "Backport of PEP 654 (exception groups)"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "exceptiongroup-1.1.0-py3-none-any.whl", hash = "sha256:327cbda3da756e2de031a3107b81ab7b3770a602c4d16ca618298c526f4bec1e"},
+ {file = "exceptiongroup-1.1.0.tar.gz", hash = "sha256:bcb67d800a4497e1b404c2dd44fca47d3b7a5e5433dbab67f96c1a685cdfdf23"},
+]
+
+[package.extras]
+test = ["pytest (>=6)"]
+
[[package]]
name = "fastapi"
version = "0.83.0"
@@ -798,14 +818,14 @@ files = [
[[package]]
name = "importlib-metadata"
-version = "5.0.0"
+version = "5.2.0"
description = "Read metadata from Python packages"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
- {file = "importlib_metadata-5.0.0-py3-none-any.whl", hash = "sha256:ddb0e35065e8938f867ed4928d0ae5bf2a53b7773871bfe6bcc7e4fcdc7dea43"},
- {file = "importlib_metadata-5.0.0.tar.gz", hash = "sha256:da31db32b304314d044d3c12c79bd59e307889b287ad12ff387b3500835fc2ab"},
+ {file = "importlib_metadata-5.2.0-py3-none-any.whl", hash = "sha256:0eafa39ba42bf225fc00e67f701d71f85aead9f878569caf13c3724f704b970f"},
+ {file = "importlib_metadata-5.2.0.tar.gz", hash = "sha256:404d48d62bba0b7a77ff9d405efd91501bef2e67ff4ace0bed40a0cf28c3c7cd"},
]
[package.dependencies]
@@ -813,20 +833,20 @@ typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""}
zipp = ">=0.5"
[package.extras]
-docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"]
+docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
perf = ["ipython"]
testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"]
[[package]]
name = "iniconfig"
-version = "1.1.1"
-description = "iniconfig: brain-dead simple config-ini parsing"
+version = "2.0.0"
+description = "brain-dead simple config-ini parsing"
category = "main"
optional = false
-python-versions = "*"
+python-versions = ">=3.7"
files = [
- {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
- {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
+ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
+ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
]
[[package]]
@@ -982,23 +1002,23 @@ files = [
[[package]]
name = "marshmallow"
-version = "3.18.0"
+version = "3.19.0"
description = "A lightweight library for converting complex datatypes to and from native Python datatypes."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
- {file = "marshmallow-3.18.0-py3-none-any.whl", hash = "sha256:35e02a3a06899c9119b785c12a22f4cda361745d66a71ab691fd7610202ae104"},
- {file = "marshmallow-3.18.0.tar.gz", hash = "sha256:6804c16114f7fce1f5b4dadc31f4674af23317fcc7f075da21e35c1a35d781f7"},
+ {file = "marshmallow-3.19.0-py3-none-any.whl", hash = "sha256:93f0958568da045b0021ec6aeb7ac37c81bfcccbb9a0e7ed8559885070b3a19b"},
+ {file = "marshmallow-3.19.0.tar.gz", hash = "sha256:90032c0fd650ce94b6ec6dc8dfeb0e3ff50c144586462c389b81a07205bedb78"},
]
[package.dependencies]
packaging = ">=17.0"
[package.extras]
-dev = ["flake8 (==5.0.4)", "flake8-bugbear (==22.9.11)", "mypy (==0.971)", "pre-commit (>=2.4,<3.0)", "pytest", "pytz", "simplejson", "tox"]
-docs = ["alabaster (==0.7.12)", "autodocsumm (==0.2.9)", "sphinx (==5.1.1)", "sphinx-issues (==3.0.1)", "sphinx-version-warning (==1.1.2)"]
-lint = ["flake8 (==5.0.4)", "flake8-bugbear (==22.9.11)", "mypy (==0.971)", "pre-commit (>=2.4,<3.0)"]
+dev = ["flake8 (==5.0.4)", "flake8-bugbear (==22.10.25)", "mypy (==0.990)", "pre-commit (>=2.4,<3.0)", "pytest", "pytz", "simplejson", "tox"]
+docs = ["alabaster (==0.7.12)", "autodocsumm (==0.2.9)", "sphinx (==5.3.0)", "sphinx-issues (==3.0.1)", "sphinx-version-warning (==1.1.2)"]
+lint = ["flake8 (==5.0.4)", "flake8-bugbear (==22.10.25)", "mypy (==0.990)", "pre-commit (>=2.4,<3.0)"]
tests = ["pytest", "pytz", "simplejson"]
[[package]]
@@ -1091,19 +1111,16 @@ attrs = ">=19.2.0"
[[package]]
name = "packaging"
-version = "21.3"
+version = "23.0"
description = "Core utilities for Python packages"
category = "main"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
files = [
- {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
- {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
+ {file = "packaging-23.0-py3-none-any.whl", hash = "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2"},
+ {file = "packaging-23.0.tar.gz", hash = "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"},
]
-[package.dependencies]
-pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
-
[[package]]
name = "pathlib2"
version = "2.3.7.post1"
@@ -1239,18 +1256,6 @@ files = [
{file = "psycopg2_binary-2.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:b4d7679a08fea64573c969f6994a2631908bb2c0e69a7235648642f3d2e39a68"},
]
-[[package]]
-name = "py"
-version = "1.11.0"
-description = "library with cross-python path, ini-parsing, io, code, log facilities"
-category = "main"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-files = [
- {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
- {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
-]
-
[[package]]
name = "pycparser"
version = "2.21"
@@ -1265,89 +1270,88 @@ files = [
[[package]]
name = "pycryptodomex"
-version = "3.14.1"
+version = "3.16.0"
description = "Cryptographic library for Python"
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
files = [
- {file = "pycryptodomex-3.14.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ca88f2f7020002638276439a01ffbb0355634907d1aa5ca91f3dc0c2e44e8f3b"},
- {file = "pycryptodomex-3.14.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:8536bc08d130cae6dcba1ea689f2913dfd332d06113904d171f2f56da6228e89"},
- {file = "pycryptodomex-3.14.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:406ec8cfe0c098fadb18d597dc2ee6de4428d640c0ccafa453f3d9b2e58d29e2"},
- {file = "pycryptodomex-3.14.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:da8db8374295fb532b4b0c467e66800ef17d100e4d5faa2bbbd6df35502da125"},
- {file = "pycryptodomex-3.14.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:d709572d64825d8d59ea112e11cc7faf6007f294e9951324b7574af4251e4de8"},
- {file = "pycryptodomex-3.14.1-cp27-cp27m-win32.whl", hash = "sha256:3da13c2535b7aea94cc2a6d1b1b37746814c74b6e80790daddd55ca5c120a489"},
- {file = "pycryptodomex-3.14.1-cp27-cp27m-win_amd64.whl", hash = "sha256:298c00ea41a81a491d5b244d295d18369e5aac4b61b77b2de5b249ca61cd6659"},
- {file = "pycryptodomex-3.14.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:77931df40bb5ce5e13f4de2bfc982b2ddc0198971fbd947776c8bb5050896eb2"},
- {file = "pycryptodomex-3.14.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:c5dd3ffa663c982d7f1be9eb494a8924f6d40e2e2f7d1d27384cfab1b2ac0662"},
- {file = "pycryptodomex-3.14.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:2aa887683eee493e015545bd69d3d21ac8d5ad582674ec98f4af84511e353e45"},
- {file = "pycryptodomex-3.14.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:8085bd0ad2034352eee4d4f3e2da985c2749cb7344b939f4d95ead38c2520859"},
- {file = "pycryptodomex-3.14.1-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:e95a4a6c54d27a84a4624d2af8bb9ee178111604653194ca6880c98dcad92f48"},
- {file = "pycryptodomex-3.14.1-cp35-abi3-manylinux1_i686.whl", hash = "sha256:a4d412eba5679ede84b41dbe48b1bed8f33131ab9db06c238a235334733acc5e"},
- {file = "pycryptodomex-3.14.1-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:d2cce1c82a7845d7e2e8a0956c6b7ed3f1661c9acf18eb120fc71e098ab5c6fe"},
- {file = "pycryptodomex-3.14.1-cp35-abi3-manylinux2010_i686.whl", hash = "sha256:f75009715dcf4a3d680c2338ab19dac5498f8121173a929872950f4fb3a48fbf"},
- {file = "pycryptodomex-3.14.1-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:1ca8e1b4c62038bb2da55451385246f51f412c5f5eabd64812c01766a5989b4a"},
- {file = "pycryptodomex-3.14.1-cp35-abi3-win32.whl", hash = "sha256:ee835def05622e0c8b1435a906491760a43d0c462f065ec9143ec4b8d79f8bff"},
- {file = "pycryptodomex-3.14.1-cp35-abi3-win_amd64.whl", hash = "sha256:b5a185ae79f899b01ca49f365bdf15a45d78d9856f09b0de1a41b92afce1a07f"},
- {file = "pycryptodomex-3.14.1-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:797a36bd1f69df9e2798e33edb4bd04e5a30478efc08f9428c087f17f65a7045"},
- {file = "pycryptodomex-3.14.1-pp27-pypy_73-manylinux1_x86_64.whl", hash = "sha256:aebecde2adc4a6847094d3bd6a8a9538ef3438a5ea84ac1983fcb167db614461"},
- {file = "pycryptodomex-3.14.1-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:f8524b8bc89470cec7ac51734907818d3620fb1637f8f8b542d650ebec42a126"},
- {file = "pycryptodomex-3.14.1-pp27-pypy_73-win32.whl", hash = "sha256:4d0db8df9ffae36f416897ad184608d9d7a8c2b46c4612c6bc759b26c073f750"},
- {file = "pycryptodomex-3.14.1-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b276cc4deb4a80f9dfd47a41ebb464b1fe91efd8b1b8620cf5ccf8b824b850d6"},
- {file = "pycryptodomex-3.14.1-pp36-pypy36_pp73-manylinux1_x86_64.whl", hash = "sha256:e36c7e3b5382cd5669cf199c4a04a0279a43b2a3bdd77627e9b89778ac9ec08c"},
- {file = "pycryptodomex-3.14.1-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:c4d8977ccda886d88dc3ca789de2f1adc714df912ff3934b3d0a3f3d777deafb"},
- {file = "pycryptodomex-3.14.1-pp36-pypy36_pp73-win32.whl", hash = "sha256:530756d2faa40af4c1f74123e1d889bd07feae45bac2fd32f259a35f7aa74151"},
- {file = "pycryptodomex-3.14.1.tar.gz", hash = "sha256:2ce76ed0081fd6ac8c74edc75b9d14eca2064173af79843c24fa62573263c1f2"},
+ {file = "pycryptodomex-3.16.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:b3d04c00d777c36972b539fb79958790126847d84ec0129fce1efef250bfe3ce"},
+ {file = "pycryptodomex-3.16.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:e5a670919076b71522c7d567a9043f66f14b202414a63c3a078b5831ae342c03"},
+ {file = "pycryptodomex-3.16.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:ce338a9703f54b2305a408fc9890eb966b727ce72b69f225898bb4e9d9ed3f1f"},
+ {file = "pycryptodomex-3.16.0-cp27-cp27m-manylinux2014_aarch64.whl", hash = "sha256:a1c0ae7123448ecb034c75c713189cb00ebe2d415b11682865b6c54d200d9c93"},
+ {file = "pycryptodomex-3.16.0-cp27-cp27m-win32.whl", hash = "sha256:8851585ff19871e5d69e1790f4ca5f6fd1699d6b8b14413b472a4c0dbc7ea780"},
+ {file = "pycryptodomex-3.16.0-cp27-cp27m-win_amd64.whl", hash = "sha256:8dd2d9e3c617d0712ed781a77efd84ea579e76c5f9b2a4bc0b684ebeddf868b2"},
+ {file = "pycryptodomex-3.16.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:2ad9bb86b355b6104796567dd44c215b3dc953ef2fae5e0bdfb8516731df92cf"},
+ {file = "pycryptodomex-3.16.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:e25a2f5667d91795f9417cb856f6df724ccdb0cdd5cbadb212ee9bf43946e9f8"},
+ {file = "pycryptodomex-3.16.0-cp27-cp27mu-manylinux2014_aarch64.whl", hash = "sha256:b0789a8490114a2936ed77c87792cfe77582c829cb43a6d86ede0f9624ba8aa3"},
+ {file = "pycryptodomex-3.16.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:0da835af786fdd1c9930994c78b23e88d816dc3f99aa977284a21bbc26d19735"},
+ {file = "pycryptodomex-3.16.0-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:22aed0868622d95179217c298e37ed7410025c7b29dac236d3230617d1e4ed56"},
+ {file = "pycryptodomex-3.16.0-cp35-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1619087fb5b31510b0b0b058a54f001a5ffd91e6ffee220d9913064519c6a69d"},
+ {file = "pycryptodomex-3.16.0-cp35-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:70288d9bfe16b2fd0d20b6c365db614428f1bcde7b20d56e74cf88ade905d9eb"},
+ {file = "pycryptodomex-3.16.0-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:7993d26dae4d83b8f4ce605bb0aecb8bee330bb3c95475ef06f3694403621e71"},
+ {file = "pycryptodomex-3.16.0-cp35-abi3-musllinux_1_1_i686.whl", hash = "sha256:1cda60207be8c1cf0b84b9138f9e3ca29335013d2b690774a5e94678ff29659a"},
+ {file = "pycryptodomex-3.16.0-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:04610536921c1ec7adba158ef570348550c9f3a40bc24be9f8da2ef7ab387981"},
+ {file = "pycryptodomex-3.16.0-cp35-abi3-win32.whl", hash = "sha256:daa67f5ebb6fbf1ee9c90decaa06ca7fc88a548864e5e484d52b0920a57fe8a5"},
+ {file = "pycryptodomex-3.16.0-cp35-abi3-win_amd64.whl", hash = "sha256:231dc8008cbdd1ae0e34645d4523da2dbc7a88c325f0d4a59635a86ee25b41dd"},
+ {file = "pycryptodomex-3.16.0-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:4dbbe18cc232b5980c7633972ae5417d0df76fe89e7db246eefd17ef4d8e6d7a"},
+ {file = "pycryptodomex-3.16.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:893f8a97d533c66cc3a56e60dd3ed40a3494ddb4aafa7e026429a08772f8a849"},
+ {file = "pycryptodomex-3.16.0-pp27-pypy_73-win32.whl", hash = "sha256:6a465e4f856d2a4f2a311807030c89166529ccf7ccc65bef398de045d49144b6"},
+ {file = "pycryptodomex-3.16.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ba57ac7861fd2c837cdb33daf822f2a052ff57dd769a2107807f52a36d0e8d38"},
+ {file = "pycryptodomex-3.16.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f2b971a7b877348a27dcfd0e772a0343fb818df00b74078e91c008632284137d"},
+ {file = "pycryptodomex-3.16.0-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e2453162f473c1eae4826eb10cd7bce19b5facac86d17fb5f29a570fde145abd"},
+ {file = "pycryptodomex-3.16.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:0ba28aa97cdd3ff5ed1a4f2b7f5cd04e721166bd75bd2b929e2734433882b583"},
+ {file = "pycryptodomex-3.16.0.tar.gz", hash = "sha256:e9ba9d8ed638733c9e95664470b71d624a6def149e2db6cc52c1aca5a6a2df1d"},
]
[[package]]
name = "pydantic"
-version = "1.10.2"
+version = "1.10.4"
description = "Data validation and settings management using python type hints"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
- {file = "pydantic-1.10.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb6ad4489af1bac6955d38ebcb95079a836af31e4c4f74aba1ca05bb9f6027bd"},
- {file = "pydantic-1.10.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a1f5a63a6dfe19d719b1b6e6106561869d2efaca6167f84f5ab9347887d78b98"},
- {file = "pydantic-1.10.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:352aedb1d71b8b0736c6d56ad2bd34c6982720644b0624462059ab29bd6e5912"},
- {file = "pydantic-1.10.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19b3b9ccf97af2b7519c42032441a891a5e05c68368f40865a90eb88833c2559"},
- {file = "pydantic-1.10.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e9069e1b01525a96e6ff49e25876d90d5a563bc31c658289a8772ae186552236"},
- {file = "pydantic-1.10.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:355639d9afc76bcb9b0c3000ddcd08472ae75318a6eb67a15866b87e2efa168c"},
- {file = "pydantic-1.10.2-cp310-cp310-win_amd64.whl", hash = "sha256:ae544c47bec47a86bc7d350f965d8b15540e27e5aa4f55170ac6a75e5f73b644"},
- {file = "pydantic-1.10.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a4c805731c33a8db4b6ace45ce440c4ef5336e712508b4d9e1aafa617dc9907f"},
- {file = "pydantic-1.10.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d49f3db871575e0426b12e2f32fdb25e579dea16486a26e5a0474af87cb1ab0a"},
- {file = "pydantic-1.10.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37c90345ec7dd2f1bcef82ce49b6235b40f282b94d3eec47e801baf864d15525"},
- {file = "pydantic-1.10.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b5ba54d026c2bd2cb769d3468885f23f43710f651688e91f5fb1edcf0ee9283"},
- {file = "pydantic-1.10.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:05e00dbebbe810b33c7a7362f231893183bcc4251f3f2ff991c31d5c08240c42"},
- {file = "pydantic-1.10.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2d0567e60eb01bccda3a4df01df677adf6b437958d35c12a3ac3e0f078b0ee52"},
- {file = "pydantic-1.10.2-cp311-cp311-win_amd64.whl", hash = "sha256:c6f981882aea41e021f72779ce2a4e87267458cc4d39ea990729e21ef18f0f8c"},
- {file = "pydantic-1.10.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c4aac8e7103bf598373208f6299fa9a5cfd1fc571f2d40bf1dd1955a63d6eeb5"},
- {file = "pydantic-1.10.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a7b66c3f499108b448f3f004801fcd7d7165fb4200acb03f1c2402da73ce4c"},
- {file = "pydantic-1.10.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bedf309630209e78582ffacda64a21f96f3ed2e51fbf3962d4d488e503420254"},
- {file = "pydantic-1.10.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9300fcbebf85f6339a02c6994b2eb3ff1b9c8c14f502058b5bf349d42447dcf5"},
- {file = "pydantic-1.10.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:216f3bcbf19c726b1cc22b099dd409aa371f55c08800bcea4c44c8f74b73478d"},
- {file = "pydantic-1.10.2-cp37-cp37m-win_amd64.whl", hash = "sha256:dd3f9a40c16daf323cf913593083698caee97df2804aa36c4b3175d5ac1b92a2"},
- {file = "pydantic-1.10.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b97890e56a694486f772d36efd2ba31612739bc6f3caeee50e9e7e3ebd2fdd13"},
- {file = "pydantic-1.10.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9cabf4a7f05a776e7793e72793cd92cc865ea0e83a819f9ae4ecccb1b8aa6116"},
- {file = "pydantic-1.10.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06094d18dd5e6f2bbf93efa54991c3240964bb663b87729ac340eb5014310624"},
- {file = "pydantic-1.10.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc78cc83110d2f275ec1970e7a831f4e371ee92405332ebfe9860a715f8336e1"},
- {file = "pydantic-1.10.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ee433e274268a4b0c8fde7ad9d58ecba12b069a033ecc4645bb6303c062d2e9"},
- {file = "pydantic-1.10.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7c2abc4393dea97a4ccbb4ec7d8658d4e22c4765b7b9b9445588f16c71ad9965"},
- {file = "pydantic-1.10.2-cp38-cp38-win_amd64.whl", hash = "sha256:0b959f4d8211fc964772b595ebb25f7652da3f22322c007b6fed26846a40685e"},
- {file = "pydantic-1.10.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c33602f93bfb67779f9c507e4d69451664524389546bacfe1bee13cae6dc7488"},
- {file = "pydantic-1.10.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5760e164b807a48a8f25f8aa1a6d857e6ce62e7ec83ea5d5c5a802eac81bad41"},
- {file = "pydantic-1.10.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6eb843dcc411b6a2237a694f5e1d649fc66c6064d02b204a7e9d194dff81eb4b"},
- {file = "pydantic-1.10.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b8795290deaae348c4eba0cebb196e1c6b98bdbe7f50b2d0d9a4a99716342fe"},
- {file = "pydantic-1.10.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e0bedafe4bc165ad0a56ac0bd7695df25c50f76961da29c050712596cf092d6d"},
- {file = "pydantic-1.10.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e05aed07fa02231dbf03d0adb1be1d79cabb09025dd45aa094aa8b4e7b9dcda"},
- {file = "pydantic-1.10.2-cp39-cp39-win_amd64.whl", hash = "sha256:c1ba1afb396148bbc70e9eaa8c06c1716fdddabaf86e7027c5988bae2a829ab6"},
- {file = "pydantic-1.10.2-py3-none-any.whl", hash = "sha256:1b6ee725bd6e83ec78b1aa32c5b1fa67a3a65badddde3976bca5fe4568f27709"},
- {file = "pydantic-1.10.2.tar.gz", hash = "sha256:91b8e218852ef6007c2b98cd861601c6a09f1aa32bbbb74fab5b1c33d4a1e410"},
+ {file = "pydantic-1.10.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5635de53e6686fe7a44b5cf25fcc419a0d5e5c1a1efe73d49d48fe7586db854"},
+ {file = "pydantic-1.10.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6dc1cc241440ed7ca9ab59d9929075445da6b7c94ced281b3dd4cfe6c8cff817"},
+ {file = "pydantic-1.10.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51bdeb10d2db0f288e71d49c9cefa609bca271720ecd0c58009bd7504a0c464c"},
+ {file = "pydantic-1.10.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:78cec42b95dbb500a1f7120bdf95c401f6abb616bbe8785ef09887306792e66e"},
+ {file = "pydantic-1.10.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8775d4ef5e7299a2f4699501077a0defdaac5b6c4321173bcb0f3c496fbadf85"},
+ {file = "pydantic-1.10.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:572066051eeac73d23f95ba9a71349c42a3e05999d0ee1572b7860235b850cc6"},
+ {file = "pydantic-1.10.4-cp310-cp310-win_amd64.whl", hash = "sha256:7feb6a2d401f4d6863050f58325b8d99c1e56f4512d98b11ac64ad1751dc647d"},
+ {file = "pydantic-1.10.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:39f4a73e5342b25c2959529f07f026ef58147249f9b7431e1ba8414a36761f53"},
+ {file = "pydantic-1.10.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:983e720704431a6573d626b00662eb78a07148c9115129f9b4351091ec95ecc3"},
+ {file = "pydantic-1.10.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75d52162fe6b2b55964fbb0af2ee58e99791a3138588c482572bb6087953113a"},
+ {file = "pydantic-1.10.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fdf8d759ef326962b4678d89e275ffc55b7ce59d917d9f72233762061fd04a2d"},
+ {file = "pydantic-1.10.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:05a81b006be15655b2a1bae5faa4280cf7c81d0e09fcb49b342ebf826abe5a72"},
+ {file = "pydantic-1.10.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d88c4c0e5c5dfd05092a4b271282ef0588e5f4aaf345778056fc5259ba098857"},
+ {file = "pydantic-1.10.4-cp311-cp311-win_amd64.whl", hash = "sha256:6a05a9db1ef5be0fe63e988f9617ca2551013f55000289c671f71ec16f4985e3"},
+ {file = "pydantic-1.10.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:887ca463c3bc47103c123bc06919c86720e80e1214aab79e9b779cda0ff92a00"},
+ {file = "pydantic-1.10.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdf88ab63c3ee282c76d652fc86518aacb737ff35796023fae56a65ced1a5978"},
+ {file = "pydantic-1.10.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a48f1953c4a1d9bd0b5167ac50da9a79f6072c63c4cef4cf2a3736994903583e"},
+ {file = "pydantic-1.10.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a9f2de23bec87ff306aef658384b02aa7c32389766af3c5dee9ce33e80222dfa"},
+ {file = "pydantic-1.10.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:cd8702c5142afda03dc2b1ee6bc358b62b3735b2cce53fc77b31ca9f728e4bc8"},
+ {file = "pydantic-1.10.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6e7124d6855b2780611d9f5e1e145e86667eaa3bd9459192c8dc1a097f5e9903"},
+ {file = "pydantic-1.10.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b53e1d41e97063d51a02821b80538053ee4608b9a181c1005441f1673c55423"},
+ {file = "pydantic-1.10.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:55b1625899acd33229c4352ce0ae54038529b412bd51c4915349b49ca575258f"},
+ {file = "pydantic-1.10.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:301d626a59edbe5dfb48fcae245896379a450d04baeed50ef40d8199f2733b06"},
+ {file = "pydantic-1.10.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6f9d649892a6f54a39ed56b8dfd5e08b5f3be5f893da430bed76975f3735d15"},
+ {file = "pydantic-1.10.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d7b5a3821225f5c43496c324b0d6875fde910a1c2933d726a743ce328fbb2a8c"},
+ {file = "pydantic-1.10.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f2f7eb6273dd12472d7f218e1fef6f7c7c2f00ac2e1ecde4db8824c457300416"},
+ {file = "pydantic-1.10.4-cp38-cp38-win_amd64.whl", hash = "sha256:4b05697738e7d2040696b0a66d9f0a10bec0efa1883ca75ee9e55baf511909d6"},
+ {file = "pydantic-1.10.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a9a6747cac06c2beb466064dda999a13176b23535e4c496c9d48e6406f92d42d"},
+ {file = "pydantic-1.10.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:eb992a1ef739cc7b543576337bebfc62c0e6567434e522e97291b251a41dad7f"},
+ {file = "pydantic-1.10.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:990406d226dea0e8f25f643b370224771878142155b879784ce89f633541a024"},
+ {file = "pydantic-1.10.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e82a6d37a95e0b1b42b82ab340ada3963aea1317fd7f888bb6b9dfbf4fff57c"},
+ {file = "pydantic-1.10.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9193d4f4ee8feca58bc56c8306bcb820f5c7905fd919e0750acdeeeef0615b28"},
+ {file = "pydantic-1.10.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2b3ce5f16deb45c472dde1a0ee05619298c864a20cded09c4edd820e1454129f"},
+ {file = "pydantic-1.10.4-cp39-cp39-win_amd64.whl", hash = "sha256:9cbdc268a62d9a98c56e2452d6c41c0263d64a2009aac69246486f01b4f594c4"},
+ {file = "pydantic-1.10.4-py3-none-any.whl", hash = "sha256:4948f264678c703f3877d1c8877c4e3b2e12e549c57795107f08cf70c6ec7774"},
+ {file = "pydantic-1.10.4.tar.gz", hash = "sha256:b9a3859f24eb4e097502a3be1fb4b2abb79b6103dd9e2e0edb70613a4459a648"},
]
[package.dependencies]
-typing-extensions = ">=4.1.0"
+typing-extensions = ">=4.2.0"
[package.extras]
dotenv = ["python-dotenv (>=0.10.4)"]
@@ -1400,21 +1404,6 @@ coincurve = ">=17.0.0,<18.0.0"
cryptography = ">=36.0.1,<37.0.0"
PySocks = ">=1.7.1,<2.0.0"
-[[package]]
-name = "pyparsing"
-version = "3.0.9"
-description = "pyparsing module - Classes and methods to define and execute parsing grammars"
-category = "main"
-optional = false
-python-versions = ">=3.6.8"
-files = [
- {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"},
- {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"},
-]
-
-[package.extras]
-diagrams = ["jinja2", "railroad-diagrams"]
-
[[package]]
name = "pypng"
version = "0.0.21"
@@ -1472,25 +1461,25 @@ files = [
[[package]]
name = "pytest"
-version = "7.1.3"
+version = "7.2.1"
description = "pytest: simple powerful testing with Python"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
- {file = "pytest-7.1.3-py3-none-any.whl", hash = "sha256:1377bda3466d70b55e3f5cecfa55bb7cfcf219c7964629b967c37cf0bda818b7"},
- {file = "pytest-7.1.3.tar.gz", hash = "sha256:4f365fec2dff9c1162f834d9f18af1ba13062db0c708bf7b946f8a5c76180c39"},
+ {file = "pytest-7.2.1-py3-none-any.whl", hash = "sha256:c7c6ca206e93355074ae32f7403e8ea12163b1163c976fee7d4d84027c162be5"},
+ {file = "pytest-7.2.1.tar.gz", hash = "sha256:d45e0952f3727241918b8fd0f376f5ff6b301cc0777c6f9a556935c92d8a7d42"},
]
[package.dependencies]
attrs = ">=19.2.0"
colorama = {version = "*", markers = "sys_platform == \"win32\""}
+exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
iniconfig = "*"
packaging = "*"
pluggy = ">=0.12,<2.0"
-py = ">=1.8.2"
-tomli = ">=1.0.0"
+tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""}
[package.extras]
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"]
@@ -1695,18 +1684,18 @@ cffi = ">=1.3.0"
[[package]]
name = "setuptools"
-version = "65.6.3"
+version = "65.7.0"
description = "Easily download, build, install, upgrade, and uninstall Python packages"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
- {file = "setuptools-65.6.3-py3-none-any.whl", hash = "sha256:57f6f22bde4e042978bcd50176fdb381d7c21a9efa4041202288d3737a0c6a54"},
- {file = "setuptools-65.6.3.tar.gz", hash = "sha256:a7620757bf984b58deaf32fc8a4577a9bbc0850cf92c20e1ce41c38c19e5fb75"},
+ {file = "setuptools-65.7.0-py3-none-any.whl", hash = "sha256:8ab4f1dbf2b4a65f7eec5ad0c620e84c34111a68d3349833494b9088212214dd"},
+ {file = "setuptools-65.7.0.tar.gz", hash = "sha256:4d3c92fac8f1118bb77a22181355e29c239cabfe2b9effdaa665c66b711136d7"},
]
[package.extras]
-docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
+docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
@@ -1795,7 +1784,7 @@ mssql = ["pyodbc"]
mssql-pymssql = ["pymssql"]
mssql-pyodbc = ["pyodbc"]
mysql = ["mysqlclient"]
-oracle = ["cx_oracle"]
+oracle = ["cx-oracle"]
postgresql = ["psycopg2"]
postgresql-pg8000 = ["pg8000 (<1.16.6)"]
postgresql-psycopg2binary = ["psycopg2-binary"]
@@ -1926,14 +1915,14 @@ files = [
[[package]]
name = "urllib3"
-version = "1.26.12"
+version = "1.26.14"
description = "HTTP library with thread-safe connection pooling, file post, and more."
category = "main"
optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4"
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
files = [
- {file = "urllib3-1.26.12-py2.py3-none-any.whl", hash = "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"},
- {file = "urllib3-1.26.12.tar.gz", hash = "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e"},
+ {file = "urllib3-1.26.14-py2.py3-none-any.whl", hash = "sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1"},
+ {file = "urllib3-1.26.14.tar.gz", hash = "sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72"},
]
[package.extras]
@@ -2056,6 +2045,21 @@ files = [
{file = "websockets-10.0.tar.gz", hash = "sha256:c4fc9a1d242317892590abe5b61a9127f1a61740477bfb121743f290b8054002"},
]
+[[package]]
+name = "wheel"
+version = "0.38.4"
+description = "A built-package format for Python"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "wheel-0.38.4-py3-none-any.whl", hash = "sha256:b60533f3f5d530e971d6737ca6d58681ee434818fab630c83a734bb10c083ce8"},
+ {file = "wheel-0.38.4.tar.gz", hash = "sha256:965f5259b566725405b05e7cf774052044b1ed30119b5d586b2703aafe8719ac"},
+]
+
+[package.extras]
+test = ["pytest (>=3.0.0)"]
+
[[package]]
name = "win32-setctime"
version = "1.1.0"
@@ -2073,14 +2077,14 @@ dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"]
[[package]]
name = "zipp"
-version = "3.9.0"
+version = "3.11.0"
description = "Backport of pathlib-compatible object wrapper for zip files"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
- {file = "zipp-3.9.0-py3-none-any.whl", hash = "sha256:972cfa31bc2fedd3fa838a51e9bc7e64b7fb725a8c00e7431554311f180e9980"},
- {file = "zipp-3.9.0.tar.gz", hash = "sha256:3a7af91c3db40ec72dd9d154ae18e008c69efe8ca88dde4f9a731bb82fe2f9eb"},
+ {file = "zipp-3.11.0-py3-none-any.whl", hash = "sha256:83a28fcb75844b5c0cdaf5aa4003c2d728c77e05f5aeabe8e95e56727005fbaa"},
+ {file = "zipp-3.11.0.tar.gz", hash = "sha256:a7a22e05929290a67401440b39690ae6563279bced5f314609d9d03798f56766"},
]
[package.extras]
@@ -2090,4 +2094,4 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools"
[metadata]
lock-version = "2.0"
python-versions = "^3.10 | ^3.9 | ^3.8 | ^3.7"
-content-hash = "73e1443abc1eed24639a5297a66b2eb16e80491f8849b8008e065f153de215c7"
+content-hash = "9daf94dd600a7e23dcefcc8752fae1694e0084e56553dc578a63272776a8fe53"
diff --git a/pyproject.toml b/pyproject.toml
index 46808433..68e06db5 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,8 +1,8 @@
[tool.poetry]
name = "lnbits"
-version = "0.1.0"
-description = ""
-authors = ["matthewcroughan "]
+version = "0.9.5.3"
+description = "LNbits, free and open-source Lightning wallet and accounts system."
+authors = ["Alan Bits "]
[tool.poetry.build]
generate-setup-file = false
@@ -12,10 +12,10 @@ script = "build.py"
python = "^3.10 | ^3.9 | ^3.8 | ^3.7"
aiofiles = "0.8.0"
asgiref = "3.4.1"
-attrs = "22.1.0"
+attrs = "22.2.0"
bech32 = "1.2.0"
bitstring = "3.1.9"
-certifi = "2022.9.24"
+certifi = "2022.12.7"
charset-normalizer = "2.0.12"
click = "8.0.4"
ecdsa = "0.18.0"
@@ -26,15 +26,15 @@ httpcore = "0.15.0"
httptools = "0.4.0"
httpx = "0.23.0"
idna = "3.4"
-importlib-metadata = "5.0.0"
+importlib-metadata = "5.2.0"
jinja2 = "3.0.1"
lnurl = "0.3.6"
markupsafe = "2.0.1"
-marshmallow = "3.18.0"
+marshmallow = "3.19.0"
outcome = "1.2.0"
psycopg2-binary = "2.9.1"
-pycryptodomex = "3.14.1"
-pydantic = "1.10.2"
+pycryptodomex = "3.16.0"
+pydantic = "1.10.4"
pypng = "0.0.21"
pyqrcode = "1.2.1"
pyScss = "1.4.0"
@@ -53,7 +53,7 @@ uvicorn = "0.18.3"
uvloop = "0.16.0"
watchgod = "0.7"
websockets = "10.0"
-zipp = "3.9.0"
+zipp = "3.11.0"
loguru = "0.6.0"
cffi = "1.15.1"
websocket-client = "1.3.3"
@@ -62,7 +62,7 @@ protobuf = "^4.21.6"
Cerberus = "^1.3.4"
async-timeout = "^4.0.2"
pyln-client = "0.11.1"
-cashu = "^0.6.0"
+cashu = "0.8.2"
[tool.poetry.dev-dependencies]
@@ -88,8 +88,7 @@ profile = "black"
[tool.mypy]
files = "lnbits"
exclude = """(?x)(
- ^lnbits/extensions/bleskomat.
- | ^lnbits/extensions/boltz.
+ ^lnbits/extensions/boltz.
| ^lnbits/wallets/lnd_grpc_files.
)"""
diff --git a/requirements.txt b/requirements.txt
index da2044d7..c5332811 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,57 +3,56 @@ anyio==3.6.2 ; python_version >= "3.7" and python_version < "4.0"
asgiref==3.4.1 ; python_version >= "3.7" and python_version < "4.0"
asn1crypto==1.5.1 ; python_version >= "3.7" and python_version < "4.0"
async-timeout==4.0.2 ; python_version >= "3.7" and python_version < "4.0"
-attrs==22.1.0 ; python_version >= "3.7" and python_version < "4.0"
+attrs==22.2.0 ; python_version >= "3.7" and python_version < "4.0"
base58==2.1.1 ; python_version >= "3.7" and python_version < "4.0"
bech32==1.2.0 ; python_version >= "3.7" and python_version < "4.0"
bitstring==3.1.9 ; python_version >= "3.7" and python_version < "4.0"
-cashu==0.6.0 ; python_version >= "3.7" and python_version < "4.0"
+cashu==0.8.2 ; python_version >= "3.7" and python_version < "4.0"
cerberus==1.3.4 ; python_version >= "3.7" and python_version < "4.0"
-certifi==2022.9.24 ; python_version >= "3.7" and python_version < "4.0"
+certifi==2022.12.7 ; python_version >= "3.7" and python_version < "4.0"
cffi==1.15.1 ; python_version >= "3.7" and python_version < "4.0"
charset-normalizer==2.0.12 ; python_version >= "3.7" and python_version < "4.0"
click==8.0.4 ; python_version >= "3.7" and python_version < "4.0"
coincurve==17.0.0 ; python_version >= "3.7" and python_version < "4.0"
-colorama==0.4.5 ; python_version >= "3.7" and python_version < "4.0" and platform_system == "Windows" or python_version >= "3.7" and python_version < "4.0" and sys_platform == "win32"
+colorama==0.4.6 ; python_version >= "3.7" and python_version < "4.0" and platform_system == "Windows" or python_version >= "3.7" and python_version < "4.0" and sys_platform == "win32"
cryptography==36.0.2 ; python_version >= "3.7" and python_version < "4.0"
ecdsa==0.18.0 ; python_version >= "3.7" and python_version < "4.0"
embit==0.4.9 ; python_version >= "3.7" and python_version < "4.0"
enum34==1.1.10 ; python_version >= "3.7" and python_version < "4.0"
environs==9.5.0 ; python_version >= "3.7" and python_version < "4.0"
+exceptiongroup==1.1.0 ; python_version >= "3.7" and python_version < "3.11"
fastapi==0.83.0 ; python_version >= "3.7" and python_version < "4.0"
-grpcio==1.50.0 ; python_version >= "3.7" and python_version < "4.0"
+grpcio==1.51.1 ; python_version >= "3.7" and python_version < "4.0"
h11==0.12.0 ; python_version >= "3.7" and python_version < "4.0"
httpcore==0.15.0 ; python_version >= "3.7" and python_version < "4.0"
httptools==0.4.0 ; python_version >= "3.7" and python_version < "4.0"
httpx==0.23.0 ; python_version >= "3.7" and python_version < "4.0"
idna==3.4 ; python_version >= "3.7" and python_version < "4.0"
-importlib-metadata==5.0.0 ; python_version >= "3.7" and python_version < "4.0"
-iniconfig==1.1.1 ; python_version >= "3.7" and python_version < "4.0"
+importlib-metadata==5.2.0 ; python_version >= "3.7" and python_version < "4.0"
+iniconfig==2.0.0 ; python_version >= "3.7" and python_version < "4.0"
jinja2==3.0.1 ; python_version >= "3.7" and python_version < "4.0"
lnurl==0.3.6 ; python_version >= "3.7" and python_version < "4.0"
loguru==0.6.0 ; python_version >= "3.7" and python_version < "4.0"
markupsafe==2.0.1 ; python_version >= "3.7" and python_version < "4.0"
-marshmallow==3.18.0 ; python_version >= "3.7" and python_version < "4.0"
+marshmallow==3.19.0 ; python_version >= "3.7" and python_version < "4.0"
outcome==1.2.0 ; python_version >= "3.7" and python_version < "4.0"
-packaging==21.3 ; python_version >= "3.7" and python_version < "4.0"
+packaging==23.0 ; python_version >= "3.7" and python_version < "4.0"
pathlib2==2.3.7.post1 ; python_version >= "3.7" and python_version < "4.0"
pluggy==1.0.0 ; python_version >= "3.7" and python_version < "4.0"
-protobuf==4.21.9 ; python_version >= "3.7" and python_version < "4.0"
+protobuf==4.21.12 ; python_version >= "3.7" and python_version < "4.0"
psycopg2-binary==2.9.1 ; python_version >= "3.7" and python_version < "4.0"
-py==1.11.0 ; python_version >= "3.7" and python_version < "4.0"
pycparser==2.21 ; python_version >= "3.7" and python_version < "4.0"
-pycryptodomex==3.14.1 ; python_version >= "3.7" and python_version < "4.0"
-pydantic==1.10.2 ; python_version >= "3.7" and python_version < "4.0"
+pycryptodomex==3.16.0 ; python_version >= "3.7" and python_version < "4.0"
+pydantic==1.10.4 ; python_version >= "3.7" and python_version < "4.0"
pyln-bolt7==1.0.246 ; python_version >= "3.7" and python_version < "4.0"
pyln-client==0.11.1 ; python_version >= "3.7" and python_version < "4.0"
pyln-proto==0.11.1 ; python_version >= "3.7" and python_version < "4.0"
-pyparsing==3.0.9 ; python_version >= "3.7" and python_version < "4.0"
pypng==0.0.21 ; python_version >= "3.7" and python_version < "4.0"
pyqrcode==1.2.1 ; python_version >= "3.7" and python_version < "4.0"
pyscss==1.4.0 ; python_version >= "3.7" and python_version < "4.0"
pysocks==1.7.1 ; python_version >= "3.7" and python_version < "4.0"
pytest-asyncio==0.19.0 ; python_version >= "3.7" and python_version < "4.0"
-pytest==7.1.3 ; python_version >= "3.7" and python_version < "4.0"
+pytest==7.2.1 ; python_version >= "3.7" and python_version < "4.0"
python-bitcoinlib==0.11.2 ; python_version >= "3.7" and python_version < "4.0"
python-dotenv==0.21.0 ; python_version >= "3.7" and python_version < "4.0"
pyyaml==5.4.1 ; python_version >= "3.7" and python_version < "4.0"
@@ -62,7 +61,7 @@ requests==2.27.1 ; python_version >= "3.7" and python_version < "4.0"
rfc3986==1.5.0 ; python_version >= "3.7" and python_version < "4.0"
rfc3986[idna2008]==1.5.0 ; python_version >= "3.7" and python_version < "4.0"
secp256k1==0.14.0 ; python_version >= "3.7" and python_version < "4.0"
-setuptools==65.6.3 ; python_version >= "3.7" and python_version < "4.0"
+setuptools==65.7.0 ; python_version >= "3.7" and python_version < "4.0"
shortuuid==1.0.1 ; python_version >= "3.7" and python_version < "4.0"
six==1.16.0 ; python_version >= "3.7" and python_version < "4.0"
sniffio==1.3.0 ; python_version >= "3.7" and python_version < "4.0"
@@ -70,13 +69,14 @@ sqlalchemy-aio==0.17.0 ; python_version >= "3.7" and python_version < "4.0"
sqlalchemy==1.3.24 ; python_version >= "3.7" and python_version < "4.0"
sse-starlette==0.6.2 ; python_version >= "3.7" and python_version < "4.0"
starlette==0.19.1 ; python_version >= "3.7" and python_version < "4.0"
-tomli==2.0.1 ; python_version >= "3.7" and python_version < "4.0"
+tomli==2.0.1 ; python_version >= "3.7" and python_version < "3.11"
typing-extensions==4.4.0 ; python_version >= "3.7" and python_version < "4.0"
-urllib3==1.26.12 ; python_version >= "3.7" and python_version < "4"
+urllib3==1.26.14 ; python_version >= "3.7" and python_version < "4.0"
uvicorn==0.18.3 ; python_version >= "3.7" and python_version < "4.0"
uvloop==0.16.0 ; python_version >= "3.7" and python_version < "4.0"
watchgod==0.7 ; python_version >= "3.7" and python_version < "4.0"
websocket-client==1.3.3 ; python_version >= "3.7" and python_version < "4.0"
websockets==10.0 ; python_version >= "3.7" and python_version < "4.0"
+wheel==0.38.4 ; python_version >= "3.7" and python_version < "4.0"
win32-setctime==1.1.0 ; python_version >= "3.7" and python_version < "4.0" and sys_platform == "win32"
-zipp==3.9.0 ; python_version >= "3.7" and python_version < "4.0"
+zipp==3.11.0 ; python_version >= "3.7" and python_version < "4.0"