From 7cd3487bc9cfee4730708f6e4fc58071148a4172 Mon Sep 17 00:00:00 2001 From: benarc Date: Wed, 17 Mar 2021 15:38:32 +0000 Subject: [PATCH 01/11] Added hash check to db --- lnbits/extensions/withdraw/migrations.py | 13 +++++++++++++ lnbits/extensions/withdraw/models.py | 8 ++++++++ 2 files changed, 21 insertions(+) diff --git a/lnbits/extensions/withdraw/migrations.py b/lnbits/extensions/withdraw/migrations.py index 4af24f8f..b0cf0b8f 100644 --- a/lnbits/extensions/withdraw/migrations.py +++ b/lnbits/extensions/withdraw/migrations.py @@ -94,3 +94,16 @@ async def m002_change_withdraw_table(db): ), ) await db.execute("DROP TABLE withdraw_links") + +async def m003_make_hash_check(db): + """ + Creates a hash check table. + """ + await db.execute( + """ + CREATE TABLE IF NOT EXISTS hash_check ( + id TEXT PRIMARY KEY, + lnurl_id TEXT + ); + """ + ) \ No newline at end of file diff --git a/lnbits/extensions/withdraw/models.py b/lnbits/extensions/withdraw/models.py index 9a4dfb7c..1903dba6 100644 --- a/lnbits/extensions/withdraw/models.py +++ b/lnbits/extensions/withdraw/models.py @@ -59,3 +59,11 @@ class WithdrawLink(NamedTuple): max_withdrawable=self.max_withdrawable * 1000, default_description=self.title, ) + +class HashCheck(NamedTuple): + id: str + lnurl_id: str + + @classmethod + def from_row(cls, row: Row) -> "Hash": + return cls(**dict(row)) \ No newline at end of file From 514329045f2cf9ab570b250e460542dd61562fd4 Mon Sep 17 00:00:00 2001 From: benarc Date: Wed, 17 Mar 2021 16:33:12 +0000 Subject: [PATCH 02/11] Should work --- lnbits/extensions/withdraw/crud.py | 25 +++++++++++++++++++++++++ lnbits/extensions/withdraw/models.py | 1 - lnbits/extensions/withdraw/views_api.py | 16 ++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/lnbits/extensions/withdraw/crud.py b/lnbits/extensions/withdraw/crud.py index 78fd7f56..64d4373e 100644 --- a/lnbits/extensions/withdraw/crud.py +++ b/lnbits/extensions/withdraw/crud.py @@ -98,3 +98,28 @@ async def delete_withdraw_link(link_id: str) -> None: def chunks(lst, n): for i in range(0, len(lst), n): yield lst[i : i + n] + +async def create_hash_check( + the_hash: str, + lnurl_id: str, +) -> HashCheck: + await db.execute( + """ + INSERT INTO hash_check ( + id, + lnurl_id + ) + VALUES (?, ?) + """, + ( + the_hash, + lnurl_id, + ), + ) + hashCheck = await get_hash_check(the_hash, lnurl_id) + row = await db.fetchone("SELECT * FROM hash_check WHERE id = ?", (the_hash,)) + return HashCheck.from_row(row) if row else None + +async def get_hash_check(the_hash: str, lnurl_id: str) -> Optional[HashCheck]: + row = await db.fetchone("SELECT * FROM hash_check WHERE id = ?", (the_hash,)) + return HashCheck.from_row(row) if row else None \ No newline at end of file diff --git a/lnbits/extensions/withdraw/models.py b/lnbits/extensions/withdraw/models.py index 1903dba6..4d147f25 100644 --- a/lnbits/extensions/withdraw/models.py +++ b/lnbits/extensions/withdraw/models.py @@ -63,7 +63,6 @@ class WithdrawLink(NamedTuple): class HashCheck(NamedTuple): id: str lnurl_id: str - @classmethod def from_row(cls, row: Row) -> "Hash": return cls(**dict(row)) \ No newline at end of file diff --git a/lnbits/extensions/withdraw/views_api.py b/lnbits/extensions/withdraw/views_api.py index cb8b7f0a..d34422ba 100644 --- a/lnbits/extensions/withdraw/views_api.py +++ b/lnbits/extensions/withdraw/views_api.py @@ -12,6 +12,8 @@ from .crud import ( get_withdraw_links, update_withdraw_link, delete_withdraw_link, + create_hash_check, + get_hash_check, ) @@ -111,3 +113,17 @@ async def api_link_delete(link_id): await delete_withdraw_link(link_id) return "", HTTPStatus.NO_CONTENT + +@withdraw_ext.route("/api/v1/links//", methods=["GET"]) +@api_check_wallet_key("invoice") +async def api_hash_retrieve(the_hash, lnurl_id): + hashCheck = await get_hash_check(the_hash, lnurl_id) + + if not hashCheck: + hashCheck = await create_hash_check(the_hash, lnurl_id) + return jsonify({"status": False}), HTTPStatus.OK + + if link.wallet != g.wallet.id: + return jsonify({"status": True}), HTTPStatus.OK + + return jsonify({"status": True}), HTTPStatus.OK From 86744ebcedadca6c54619c3c73ac8267b24efbc3 Mon Sep 17 00:00:00 2001 From: benarc Date: Wed, 17 Mar 2021 16:56:23 +0000 Subject: [PATCH 03/11] Bug --- lnbits/extensions/withdraw/crud.py | 2 +- .../templates/withdraw/_api_docs.html | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/lnbits/extensions/withdraw/crud.py b/lnbits/extensions/withdraw/crud.py index 64d4373e..cc7fe387 100644 --- a/lnbits/extensions/withdraw/crud.py +++ b/lnbits/extensions/withdraw/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 WithdrawLink +from .models import WithdrawLink, HashCheck async def create_withdraw_link( diff --git a/lnbits/extensions/withdraw/templates/withdraw/_api_docs.html b/lnbits/extensions/withdraw/templates/withdraw/_api_docs.html index 18a0a542..75ed9e02 100644 --- a/lnbits/extensions/withdraw/templates/withdraw/_api_docs.html +++ b/lnbits/extensions/withdraw/templates/withdraw/_api_docs.html @@ -150,3 +150,30 @@ + + + + GET + /withdraw/api/v1/links/<the_hash>/<lnurl_id> +
Headers
+ {"X-Api-Key": <invoice_key>}
+
Body (application/json)
+
+ Returns 201 CREATED (application/json) +
+ {"status": <bool>} +
Curl example
+ curl -X GET {{ request.url_root }}api/v1/links/<the_hash>/<lnurl_id> -H + "X-Api-Key: {{ g.user.wallets[0].inkey }}" + +
+
+
From f42dab5e309c5e3d90e597492d7063035705f816 Mon Sep 17 00:00:00 2001 From: benarc Date: Wed, 17 Mar 2021 17:55:00 +0000 Subject: [PATCH 04/11] Added api docs --- .../templates/withdraw/_api_docs.html | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/lnbits/extensions/withdraw/templates/withdraw/_api_docs.html b/lnbits/extensions/withdraw/templates/withdraw/_api_docs.html index 75ed9e02..13d2c1d3 100644 --- a/lnbits/extensions/withdraw/templates/withdraw/_api_docs.html +++ b/lnbits/extensions/withdraw/templates/withdraw/_api_docs.html @@ -149,8 +149,7 @@ - - + + + + GET + /withdraw/img/<lnurl_id> +
Body (application/json)
+
+ Returns 201 CREATED (application/json) +
+ {"status": <bool>} +
Curl example
+ curl -X GET {{ request.url_root }}/withdraw/img/<lnurl_id>" + +
+
+
+ + + From 09f89d07dd32fd0b0a75f3e09674e38593ea4120 Mon Sep 17 00:00:00 2001 From: benarc Date: Wed, 17 Mar 2021 17:57:12 +0000 Subject: [PATCH 05/11] API docs --- .../extensions/withdraw/templates/withdraw/_api_docs.html | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lnbits/extensions/withdraw/templates/withdraw/_api_docs.html b/lnbits/extensions/withdraw/templates/withdraw/_api_docs.html index 13d2c1d3..e15355c3 100644 --- a/lnbits/extensions/withdraw/templates/withdraw/_api_docs.html +++ b/lnbits/extensions/withdraw/templates/withdraw/_api_docs.html @@ -129,7 +129,7 @@ dense expand-separator label="Delete a withdraw link" - class="q-pb-md" + > @@ -181,6 +181,7 @@ group="api" dense expand-separator label="Get image to embed" +class="q-pb-md" > @@ -188,10 +189,6 @@ label="Get image to embed" >GET /withdraw/img/<lnurl_id> -
Body (application/json)
-
- Returns 201 CREATED (application/json) -
{"status": <bool>}
Curl example
Date: Wed, 17 Mar 2021 17:58:03 +0000 Subject: [PATCH 06/11] api doc --- lnbits/extensions/withdraw/templates/withdraw/_api_docs.html | 1 - 1 file changed, 1 deletion(-) diff --git a/lnbits/extensions/withdraw/templates/withdraw/_api_docs.html b/lnbits/extensions/withdraw/templates/withdraw/_api_docs.html index e15355c3..b8fa8231 100644 --- a/lnbits/extensions/withdraw/templates/withdraw/_api_docs.html +++ b/lnbits/extensions/withdraw/templates/withdraw/_api_docs.html @@ -189,7 +189,6 @@ class="q-pb-md" >GET /withdraw/img/<lnurl_id> - {"status": <bool>}
Curl example
curl -X GET {{ request.url_root }}/withdraw/img/<lnurl_id>" From 66cde0154b591fcfeaffc71796cb046f8862d2cf Mon Sep 17 00:00:00 2001 From: benarc Date: Wed, 17 Mar 2021 18:02:52 +0000 Subject: [PATCH 07/11] api docs --- lnbits/extensions/withdraw/views_api.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/lnbits/extensions/withdraw/views_api.py b/lnbits/extensions/withdraw/views_api.py index d34422ba..757603c6 100644 --- a/lnbits/extensions/withdraw/views_api.py +++ b/lnbits/extensions/withdraw/views_api.py @@ -122,8 +122,5 @@ async def api_hash_retrieve(the_hash, lnurl_id): if not hashCheck: hashCheck = await create_hash_check(the_hash, lnurl_id) return jsonify({"status": False}), HTTPStatus.OK - - if link.wallet != g.wallet.id: - return jsonify({"status": True}), HTTPStatus.OK return jsonify({"status": True}), HTTPStatus.OK From ad545e7fe13e3b9dc94cfd39e745acd960f7e49a Mon Sep 17 00:00:00 2001 From: benarc Date: Wed, 17 Mar 2021 19:27:52 +0000 Subject: [PATCH 08/11] prettier/black --- lnbits/extensions/withdraw/crud.py | 2 + lnbits/extensions/withdraw/migrations.py | 1 + lnbits/extensions/withdraw/models.py | 2 + .../templates/withdraw/_api_docs.html | 91 +++++++++---------- lnbits/extensions/withdraw/views_api.py | 1 + 5 files changed, 48 insertions(+), 49 deletions(-) diff --git a/lnbits/extensions/withdraw/crud.py b/lnbits/extensions/withdraw/crud.py index cc7fe387..11f7d949 100644 --- a/lnbits/extensions/withdraw/crud.py +++ b/lnbits/extensions/withdraw/crud.py @@ -99,6 +99,7 @@ def chunks(lst, n): for i in range(0, len(lst), n): yield lst[i : i + n] + async def create_hash_check( the_hash: str, lnurl_id: str, @@ -120,6 +121,7 @@ async def create_hash_check( row = await db.fetchone("SELECT * FROM hash_check WHERE id = ?", (the_hash,)) return HashCheck.from_row(row) if row else None + async def get_hash_check(the_hash: str, lnurl_id: str) -> Optional[HashCheck]: row = await db.fetchone("SELECT * FROM hash_check WHERE id = ?", (the_hash,)) return HashCheck.from_row(row) if row else None \ No newline at end of file diff --git a/lnbits/extensions/withdraw/migrations.py b/lnbits/extensions/withdraw/migrations.py index b0cf0b8f..2cb802d4 100644 --- a/lnbits/extensions/withdraw/migrations.py +++ b/lnbits/extensions/withdraw/migrations.py @@ -95,6 +95,7 @@ async def m002_change_withdraw_table(db): ) await db.execute("DROP TABLE withdraw_links") + async def m003_make_hash_check(db): """ Creates a hash check table. diff --git a/lnbits/extensions/withdraw/models.py b/lnbits/extensions/withdraw/models.py index 4d147f25..4c485ea3 100644 --- a/lnbits/extensions/withdraw/models.py +++ b/lnbits/extensions/withdraw/models.py @@ -60,9 +60,11 @@ class WithdrawLink(NamedTuple): default_description=self.title, ) + class HashCheck(NamedTuple): id: str lnurl_id: str + @classmethod def from_row(cls, row: Row) -> "Hash": return cls(**dict(row)) \ No newline at end of file diff --git a/lnbits/extensions/withdraw/templates/withdraw/_api_docs.html b/lnbits/extensions/withdraw/templates/withdraw/_api_docs.html index b8fa8231..c303b091 100644 --- a/lnbits/extensions/withdraw/templates/withdraw/_api_docs.html +++ b/lnbits/extensions/withdraw/templates/withdraw/_api_docs.html @@ -129,7 +129,6 @@ dense expand-separator label="Delete a withdraw link" - > @@ -149,53 +148,47 @@ + + + + GET + /withdraw/api/v1/links/<the_hash>/<lnurl_id> +
Headers
+ {"X-Api-Key": <invoice_key>}
+
Body (application/json)
+
+ Returns 201 CREATED (application/json) +
+ {"status": <bool>} +
Curl example
+ curl -X GET {{ request.url_root + }}api/v1/links/<the_hash>/<lnurl_id> -H "X-Api-Key: {{ + g.user.wallets[0].inkey }}" + +
+
+
- - - GET - /withdraw/api/v1/links/<the_hash>/<lnurl_id> -
Headers
- {"X-Api-Key": <invoice_key>}
-
Body (application/json)
-
- Returns 201 CREATED (application/json) -
- {"status": <bool>} -
Curl example
- curl -X GET {{ request.url_root }}api/v1/links/<the_hash>/<lnurl_id> -H - "X-Api-Key: {{ g.user.wallets[0].inkey }}" - -
-
+ group="api" + dense + expand-separator + label="Get image to embed" + class="q-pb-md" + > + + + GET + /withdraw/img/<lnurl_id> +
Curl example
+ curl -X GET {{ request.url_root }}/withdraw/img/<lnurl_id>" + +
+
+
- - - - GET - /withdraw/img/<lnurl_id> -
Curl example
- curl -X GET {{ request.url_root }}/withdraw/img/<lnurl_id>" - -
-
-
- - - diff --git a/lnbits/extensions/withdraw/views_api.py b/lnbits/extensions/withdraw/views_api.py index 757603c6..2f80aab5 100644 --- a/lnbits/extensions/withdraw/views_api.py +++ b/lnbits/extensions/withdraw/views_api.py @@ -114,6 +114,7 @@ async def api_link_delete(link_id): return "", HTTPStatus.NO_CONTENT + @withdraw_ext.route("/api/v1/links//", methods=["GET"]) @api_check_wallet_key("invoice") async def api_hash_retrieve(the_hash, lnurl_id): From c86bd74506c054e8a0f6c271918d15d81c4cbdf3 Mon Sep 17 00:00:00 2001 From: benarc Date: Wed, 17 Mar 2021 19:32:37 +0000 Subject: [PATCH 09/11] edited api --- .../extensions/withdraw/templates/withdraw/_api_docs.html | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lnbits/extensions/withdraw/templates/withdraw/_api_docs.html b/lnbits/extensions/withdraw/templates/withdraw/_api_docs.html index c303b091..484464ba 100644 --- a/lnbits/extensions/withdraw/templates/withdraw/_api_docs.html +++ b/lnbits/extensions/withdraw/templates/withdraw/_api_docs.html @@ -148,7 +148,12 @@
- + Date: Wed, 17 Mar 2021 19:54:17 +0000 Subject: [PATCH 10/11] Ran black on bleskomat --- lnbits/extensions/bleskomat/crud.py | 29 +++++++++-- lnbits/extensions/bleskomat/exchange_rates.py | 23 ++++---- lnbits/extensions/bleskomat/helpers.py | 52 ++++++------------- lnbits/extensions/bleskomat/lnurl_api.py | 13 ++--- lnbits/extensions/bleskomat/models.py | 14 ++--- lnbits/extensions/bleskomat/views.py | 3 +- lnbits/extensions/bleskomat/views_api.py | 10 ++-- lnbits/extensions/withdraw/crud.py | 1 + 8 files changed, 70 insertions(+), 75 deletions(-) diff --git a/lnbits/extensions/bleskomat/crud.py b/lnbits/extensions/bleskomat/crud.py index 470d87cb..69079350 100644 --- a/lnbits/extensions/bleskomat/crud.py +++ b/lnbits/extensions/bleskomat/crud.py @@ -6,19 +6,30 @@ from . import db from .models import Bleskomat, BleskomatLnurl from .helpers import generate_bleskomat_lnurl_hash + async def create_bleskomat( *, wallet_id: str, name: str, fiat_currency: str, exchange_rate_provider: str, fee: str ) -> Bleskomat: bleskomat_id = uuid4().hex api_key_id = secrets.token_hex(8) api_key_secret = secrets.token_hex(32) - api_key_encoding = "hex"; + api_key_encoding = "hex" await db.execute( """ INSERT INTO bleskomats (id, wallet, api_key_id, api_key_secret, api_key_encoding, name, fiat_currency, exchange_rate_provider, fee) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) """, - (bleskomat_id, wallet_id, api_key_id, api_key_secret, api_key_encoding, name, fiat_currency, exchange_rate_provider, fee), + ( + bleskomat_id, + wallet_id, + api_key_id, + api_key_secret, + api_key_encoding, + name, + fiat_currency, + exchange_rate_provider, + fee, + ), ) bleskomat = await get_bleskomat(bleskomat_id) assert bleskomat, "Newly created bleskomat couldn't be retrieved" @@ -65,7 +76,19 @@ async def create_bleskomat_lnurl( INSERT INTO bleskomat_lnurls (id, bleskomat, wallet, hash, tag, params, api_key_id, initial_uses, remaining_uses, created_time, updated_time) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, - (bleskomat_lnurl_id, bleskomat.id, bleskomat.wallet, hash, tag, params, bleskomat.api_key_id, uses, uses, now, now), + ( + bleskomat_lnurl_id, + bleskomat.id, + bleskomat.wallet, + hash, + tag, + params, + bleskomat.api_key_id, + uses, + uses, + now, + now, + ), ) bleskomat_lnurl = await get_bleskomat_lnurl(secret) assert bleskomat_lnurl, "Newly created bleskomat LNURL couldn't be retrieved" diff --git a/lnbits/extensions/bleskomat/exchange_rates.py b/lnbits/extensions/bleskomat/exchange_rates.py index 15547370..6d9297c6 100644 --- a/lnbits/extensions/bleskomat/exchange_rates.py +++ b/lnbits/extensions/bleskomat/exchange_rates.py @@ -2,39 +2,41 @@ import httpx import json import os -fiat_currencies = json.load(open(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'fiat_currencies.json'), 'r')) +fiat_currencies = json.load( + open(os.path.join(os.path.dirname(os.path.realpath(__file__)), "fiat_currencies.json"), "r") +) exchange_rate_providers = { "bitfinex": { "name": "Bitfinex", "domain": "bitfinex.com", "api_url": "https://api.bitfinex.com/v1/pubticker/{from}{to}", - "getter": lambda data, replacements: data["last_price"] + "getter": lambda data, replacements: data["last_price"], }, "bitstamp": { "name": "Bitstamp", "domain": "bitstamp.net", "api_url": "https://www.bitstamp.net/api/v2/ticker/{from}{to}/", - "getter": lambda data, replacements: data["last"] + "getter": lambda data, replacements: data["last"], }, "coinbase": { "name": "Coinbase", "domain": "coinbase.com", "api_url": "https://api.coinbase.com/v2/exchange-rates?currency={FROM}", - "getter": lambda data, replacements: data["data"]["rates"][replacements["TO"]] + "getter": lambda data, replacements: data["data"]["rates"][replacements["TO"]], }, "coinmate": { "name": "CoinMate", "domain": "coinmate.io", "api_url": "https://coinmate.io/api/ticker?currencyPair={FROM}_{TO}", - "getter": lambda data, replacements: data["data"]["last"] + "getter": lambda data, replacements: data["data"]["last"], }, "kraken": { "name": "Kraken", "domain": "kraken.com", "api_url": "https://api.kraken.com/0/public/Ticker?pair=XBT{TO}", - "getter": lambda data, replacements: data["result"]["XXBTZ" + replacements["TO"]]["c"][0] - } + "getter": lambda data, replacements: data["result"]["XXBTZ" + replacements["TO"]]["c"][0], + }, } exchange_rate_providers_serializable = {} @@ -48,12 +50,7 @@ for ref, exchange_rate_provider in exchange_rate_providers.items(): async def fetch_fiat_exchange_rate(currency: str, provider: str): - replacements = { - "FROM" : "BTC", - "from" : "btc", - "TO" : currency.upper(), - "to" : currency.lower() - } + replacements = {"FROM": "BTC", "from": "btc", "TO": currency.upper(), "to": currency.lower()} url = exchange_rate_providers[provider]["api_url"] for key in replacements.keys(): diff --git a/lnbits/extensions/bleskomat/helpers.py b/lnbits/extensions/bleskomat/helpers.py index 439795be..9b745c79 100644 --- a/lnbits/extensions/bleskomat/helpers.py +++ b/lnbits/extensions/bleskomat/helpers.py @@ -21,11 +21,7 @@ def generate_bleskomat_lnurl_signature(payload: str, api_key_secret: str, api_ke key = base64.b64decode(api_key_secret) else: key = bytes(f"{api_key_secret}") - return hmac.new( - key=key, - msg=payload.encode(), - digestmod=hashlib.sha256 - ).hexdigest() + return hmac.new(key=key, msg=payload.encode(), digestmod=hashlib.sha256).hexdigest() def generate_bleskomat_lnurl_secret(api_key_id: str, signature: str): @@ -58,19 +54,21 @@ class LnurlValidationError(Exception): def prepare_lnurl_params(tag: str, query: Dict[str, str]): params = {} if not is_supported_lnurl_subprotocol(tag): - raise LnurlValidationError(f"Unsupported subprotocol: \"{tag}\"") + raise LnurlValidationError(f'Unsupported subprotocol: "{tag}"') if tag == "withdrawRequest": params["minWithdrawable"] = float(query["minWithdrawable"]) params["maxWithdrawable"] = float(query["maxWithdrawable"]) params["defaultDescription"] = query["defaultDescription"] if not params["minWithdrawable"] > 0: - raise LnurlValidationError("\"minWithdrawable\" must be greater than zero") + raise LnurlValidationError('"minWithdrawable" must be greater than zero') if not params["maxWithdrawable"] >= params["minWithdrawable"]: - raise LnurlValidationError("\"maxWithdrawable\" must be greater than or equal to \"minWithdrawable\"") + raise LnurlValidationError('"maxWithdrawable" must be greater than or equal to "minWithdrawable"') return params encode_uri_component_safe_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.!~*'()" + + def query_to_signing_payload(query: Dict[str, str]) -> str: # Sort the query by key, then stringify it to create the payload. sorted_keys = sorted(query.keys(), key=str.lower) @@ -84,35 +82,17 @@ def query_to_signing_payload(query: Dict[str, str]) -> str: unshorten_rules = { - "query": { - "n": "nonce", - "s": "signature", - "t": "tag" - }, - "tags": { - "c": "channelRequest", - "l": "login", - "p": "payRequest", - "w": "withdrawRequest" - }, + "query": {"n": "nonce", "s": "signature", "t": "tag"}, + "tags": {"c": "channelRequest", "l": "login", "p": "payRequest", "w": "withdrawRequest"}, "params": { - "channelRequest": { - "pl": "localAmt", - "pp": "pushAmt" - }, + "channelRequest": {"pl": "localAmt", "pp": "pushAmt"}, "login": {}, - "payRequest": { - "pn": "minSendable", - "px": "maxSendable", - "pm": "metadata" - }, - "withdrawRequest": { - "pn": "minWithdrawable", - "px": "maxWithdrawable", - "pd": "defaultDescription" - } - } + "payRequest": {"pn": "minSendable", "px": "maxSendable", "pm": "metadata"}, + "withdrawRequest": {"pn": "minWithdrawable", "px": "maxWithdrawable", "pd": "defaultDescription"}, + }, } + + def unshorten_lnurl_query(query: Dict[str, str]) -> Dict[str, str]: new_query = {} rules = unshorten_rules @@ -121,14 +101,14 @@ def unshorten_lnurl_query(query: Dict[str, str]) -> Dict[str, str]: elif "t" in query: tag = query["t"] else: - raise LnurlValidationError("Missing required query parameter: \"tag\"") + raise LnurlValidationError('Missing required query parameter: "tag"') # Unshorten tag: if tag in rules["tags"]: long_tag = rules["tags"][tag] new_query["tag"] = long_tag tag = long_tag if not tag in rules["params"]: - raise LnurlValidationError(f"Unknown tag: \"{tag}\"") + raise LnurlValidationError(f'Unknown tag: "{tag}"') for key in query: if key in rules["params"][tag]: short_param_key = key diff --git a/lnbits/extensions/bleskomat/lnurl_api.py b/lnbits/extensions/bleskomat/lnurl_api.py index 35d93032..c7df81d1 100644 --- a/lnbits/extensions/bleskomat/lnurl_api.py +++ b/lnbits/extensions/bleskomat/lnurl_api.py @@ -47,7 +47,7 @@ async def api_bleskomat_lnurl(): # The API key ID, nonce, and tag should be present in the query string. for field in ["id", "nonce", "tag"]: if not field in query: - raise LnurlHttpError(f"Failed API key signature check: Missing \"{field}\"", HTTPStatus.BAD_REQUEST) + raise LnurlHttpError(f'Failed API key signature check: Missing "{field}"', HTTPStatus.BAD_REQUEST) # URL signing scheme is described here: # https://github.com/chill117/lnurl-node#how-to-implement-url-signing-scheme @@ -72,8 +72,7 @@ async def api_bleskomat_lnurl(): params = prepare_lnurl_params(tag, query) if "f" in query: rate = await fetch_fiat_exchange_rate( - currency=query["f"], - provider=bleskomat.exchange_rate_provider + currency=query["f"], provider=bleskomat.exchange_rate_provider ) # Convert fee (%) to decimal: fee = float(bleskomat.fee) / 100 @@ -88,13 +87,7 @@ async def api_bleskomat_lnurl(): raise LnurlHttpError(e.message, HTTPStatus.BAD_REQUEST) # Create a new LNURL using the query parameters provided in the signed URL. params = json.JSONEncoder().encode(params) - lnurl = await create_bleskomat_lnurl( - bleskomat=bleskomat, - secret=secret, - tag=tag, - params=params, - uses=1 - ) + lnurl = await create_bleskomat_lnurl(bleskomat=bleskomat, secret=secret, tag=tag, params=params, uses=1) # Reply with LNURL response object. return jsonify(lnurl.get_info_response_object(secret)), HTTPStatus.OK diff --git a/lnbits/extensions/bleskomat/models.py b/lnbits/extensions/bleskomat/models.py index 8a671a71..8ea97338 100644 --- a/lnbits/extensions/bleskomat/models.py +++ b/lnbits/extensions/bleskomat/models.py @@ -39,7 +39,7 @@ class BleskomatLnurl(NamedTuple): def get_info_response_object(self, secret: str) -> Dict[str, str]: tag = self.tag params = json.loads(self.params) - response = { "tag": tag } + response = {"tag": tag} if tag == "withdrawRequest": for key in ["minWithdrawable", "maxWithdrawable", "defaultDescription"]: response[key] = params[key] @@ -54,7 +54,7 @@ class BleskomatLnurl(NamedTuple): if tag == "withdrawRequest": for field in ["pr"]: if not field in query: - raise LnurlValidationError(f"Missing required parameter: \"{field}\"") + raise LnurlValidationError(f'Missing required parameter: "{field}"') # Check the bolt11 invoice(s) provided. pr = query["pr"] if "," in pr: @@ -62,13 +62,13 @@ class BleskomatLnurl(NamedTuple): try: invoice = bolt11.decode(pr) except ValueError as e: - raise LnurlValidationError("Invalid parameter (\"pr\"): Lightning payment request expected") + raise LnurlValidationError('Invalid parameter ("pr"): Lightning payment request expected') if invoice.amount_msat < params["minWithdrawable"]: - raise LnurlValidationError("Amount in invoice must be greater than or equal to \"minWithdrawable\"") + raise LnurlValidationError('Amount in invoice must be greater than or equal to "minWithdrawable"') if invoice.amount_msat > params["maxWithdrawable"]: - raise LnurlValidationError("Amount in invoice must be less than or equal to \"maxWithdrawable\"") + raise LnurlValidationError('Amount in invoice must be less than or equal to "maxWithdrawable"') else: - raise LnurlValidationError(f"Unknown subprotocol: \"{tag}\"") + raise LnurlValidationError(f'Unknown subprotocol: "{tag}"') async def execute_action(self, query: Dict[str, str]): self.validate_action(query) @@ -105,6 +105,6 @@ class BleskomatLnurl(NamedTuple): WHERE id = ? AND remaining_uses > 0 """, - (now, self.id) + (now, self.id), ) return result.rowcount > 0 diff --git a/lnbits/extensions/bleskomat/views.py b/lnbits/extensions/bleskomat/views.py index 16e986ee..52f63499 100644 --- a/lnbits/extensions/bleskomat/views.py +++ b/lnbits/extensions/bleskomat/views.py @@ -7,6 +7,7 @@ from . import bleskomat_ext from .exchange_rates import exchange_rate_providers_serializable, fiat_currencies from .helpers import get_callback_url + @bleskomat_ext.route("/") @validate_uuids(["usr"], required=True) @check_user_exists() @@ -14,6 +15,6 @@ async def index(): bleskomat_vars = { "callback_url": get_callback_url(), "exchange_rate_providers": exchange_rate_providers_serializable, - "fiat_currencies": fiat_currencies + "fiat_currencies": fiat_currencies, } return await render_template("bleskomat/index.html", user=g.user, bleskomat_vars=bleskomat_vars) diff --git a/lnbits/extensions/bleskomat/views_api.py b/lnbits/extensions/bleskomat/views_api.py index aed4d02e..8256ece8 100644 --- a/lnbits/extensions/bleskomat/views_api.py +++ b/lnbits/extensions/bleskomat/views_api.py @@ -58,13 +58,13 @@ async def api_bleskomat_create_or_update(bleskomat_id=None): try: fiat_currency = g.data["fiat_currency"] exchange_rate_provider = g.data["exchange_rate_provider"] - rate = await fetch_fiat_exchange_rate( - currency=fiat_currency, - provider=exchange_rate_provider - ) + rate = await fetch_fiat_exchange_rate(currency=fiat_currency, provider=exchange_rate_provider) except Exception as e: print(e) - return jsonify({"message": f"Failed to fetch BTC/{fiat_currency} currency pair from \"{exchange_rate_provider}\""}), HTTPStatus.INTERNAL_SERVER_ERROR + return ( + jsonify({"message": f'Failed to fetch BTC/{fiat_currency} currency pair from "{exchange_rate_provider}"'}), + HTTPStatus.INTERNAL_SERVER_ERROR, + ) if bleskomat_id: bleskomat = await get_bleskomat(bleskomat_id) diff --git a/lnbits/extensions/withdraw/crud.py b/lnbits/extensions/withdraw/crud.py index 11f7d949..18f77db0 100644 --- a/lnbits/extensions/withdraw/crud.py +++ b/lnbits/extensions/withdraw/crud.py @@ -124,4 +124,5 @@ async def create_hash_check( async def get_hash_check(the_hash: str, lnurl_id: str) -> Optional[HashCheck]: row = await db.fetchone("SELECT * FROM hash_check WHERE id = ?", (the_hash,)) + return HashCheck.from_row(row) if row else None \ No newline at end of file From 1e9151cedcae72fd768d998230ba88dea5c87bdc Mon Sep 17 00:00:00 2001 From: benarc Date: Wed, 17 Mar 2021 19:59:00 +0000 Subject: [PATCH 11/11] ran prettier on captcha --- .../extensions/captcha/static/js/captcha.js | 131 ++++++++++-------- .../captcha/templates/captcha/display.html | 8 +- .../captcha/templates/captcha/index.html | 45 +++--- 3 files changed, 106 insertions(+), 78 deletions(-) diff --git a/lnbits/extensions/captcha/static/js/captcha.js b/lnbits/extensions/captcha/static/js/captcha.js index 6d86e865..b2387289 100644 --- a/lnbits/extensions/captcha/static/js/captcha.js +++ b/lnbits/extensions/captcha/static/js/captcha.js @@ -1,65 +1,80 @@ var ciframeLoaded = !1, - captchaStyleAdded = !1; + captchaStyleAdded = !1 function ccreateIframeElement(t = {}) { - const e = document.createElement("iframe"); - // e.style.marginLeft = "25px", - e.style.border = "none", e.style.width = "100%", e.style.height = "100%", e.scrolling = "no", e.id = "captcha-iframe"; - t.dest, t.amount, t.currency, t.label, t.opReturn; - var captchaid = document.getElementById("captchascript").getAttribute("data-captchaid"); - return e.src = "http://localhost:5000/captcha/" + captchaid, e + const e = document.createElement('iframe') + // e.style.marginLeft = "25px", + ;(e.style.border = 'none'), + (e.style.width = '100%'), + (e.style.height = '100%'), + (e.scrolling = 'no'), + (e.id = 'captcha-iframe') + t.dest, t.amount, t.currency, t.label, t.opReturn + var captchaid = document + .getElementById('captchascript') + .getAttribute('data-captchaid') + return (e.src = 'http://localhost:5000/captcha/' + captchaid), e } -document.addEventListener("DOMContentLoaded", function() { - if (captchaStyleAdded) console.log("Captcha stuff already added!"); - else { - console.log("Adding captcha stuff"), captchaStyleAdded = !0; - var t = document.createElement("style"); - t.innerHTML = "\t/*Button*/\t\t.button-captcha-filled\t\t\t{\t\t\tdisplay: flex;\t\t\talign-items: center;\t\t\tjustify-content: center;\t\t\twidth: 120px;\t\t\tmin-width: 30px;\t\t\theight: 40px;\t\t\tline-height: 2.5;\t\t\ttext-align: center;\t\t\tcursor: pointer;\t\t\t/* Rectangle 2: */\t\t\tbackground: #FF7979;\t\t\tbox-shadow: 0 2px 4px 0 rgba(0,0,0,0.20);\t\t\tborder-radius: 20px;\t\t\t/* Sign up: */\t\t\tfont-family: 'Avenir-Heavy', Futura, Helvetica, Arial;\t\t\tfont-size: 16px;\t\t\tcolor: #FFFFFF;\t\t}\t\t.button-captcha-filled:hover\t\t{\t\t\tbackground:#FFFFFF;\t\t\tcolor: #FF7979;\t\t\tbox-shadow: 0 0 4px 0 rgba(0,0,0,0.20);\t\t}\t\t.button-captcha-filled:active\t\t{\t\t\tbackground:#FFFFFF;\t\t\tcolor: #FF7979;\t\t\t/*Move it down a little bit*/\t\t\tposition: relative;\t\t\ttop: 1px;\t\t}\t\t.button-captcha-filled-dark\t\t\t{\t\t\tdisplay: flex;\t\t\talign-items: center;\t\t\tjustify-content: center;\t\t\twidth: 120px;\t\t\tmin-width: 30px;\t\t\theight: 40px;\t\t\tline-height: 2.5;\t\t\ttext-align: center;\t\t\tcursor: pointer;\t\t\t/* Rectangle 2: */\t\t\tbackground: #161C38;\t\t\tbox-shadow: 0 0px 4px 0 rgba(0,0,0,0.20);\t\t\tborder-radius: 20px;\t\t\t/* Sign up: */\t\t\tfont-family: 'Avenir-Heavy', Futura, Helvetica, Arial;\t\t\tfont-size: 16px;\t\t\tcolor: #FFFFFF;\t\t}\t\t.button-captcha-filled-dark:hover\t\t{\t\t\tbackground:#FFFFFF;\t\t\tcolor: #161C38;\t\t\tbox-shadow: 0 0px 4px 0 rgba(0,0,0,0.20);\t\t}\t\t.button-captcha-filled-dark:active\t\t{\t\t\tbackground:#FFFFFF;\t\t\tcolor: #161C38;\t\t\t/*Move it down a little bit*/\t\t\tposition: relative;\t\t\ttop: 1px;\t\t}\t\t.modal-captcha-container {\t\t position: fixed;\t\t z-index: 1000;\t\t text-align: left;/*Si no añado esto, a veces hereda el text-align:center del body, y entonces el popup queda movido a la derecha, por center + margin left que aplico*/\t\t left: 0;\t\t top: 0;\t\t width: 100%;\t\t height: 100%;\t\t background-color: rgba(0, 0, 0, 0.5);\t\t opacity: 0;\t\t visibility: hidden;\t\t transform: scale(1.1);\t\t transition: visibility 0s linear 0.25s, opacity 0.25s 0s, transform 0.25s;\t\t}\t\t.modal-captcha-content {\t\t position: absolute;\t\t top: 50%;\t\t left: 50%;\t\t transform: translate(-50%, -50%);\t\t background-color: white;\t\t width: 100%;\t\t height: 100%;\t\t border-radius: 0.5rem;\t\t /*Rounded shadowed borders*/\t\t\tbox-shadow: 2px 2px 4px 0 rgba(0,0,0,0.15);\t\t\tborder-radius: 5px;\t\t}\t\t.close-button-captcha {\t\t float: right;\t\t width: 1.5rem;\t\t line-height: 1.5rem;\t\t text-align: center;\t\t cursor: pointer;\t\t margin-right:20px;\t\t margin-top:10px;\t\t border-radius: 0.25rem;\t\t background-color: lightgray;\t\t}\t\t.close-button-captcha:hover {\t\t background-color: darkgray;\t\t}\t\t.show-modal-captcha {\t\t opacity: 1;\t\t visibility: visible;\t\t transform: scale(1.0);\t\t transition: visibility 0s linear 0s, opacity 0.25s 0s, transform 0.25s;\t\t}\t\t/* Mobile */\t\t@media screen and (min-device-width: 160px) and ( max-width: 1077px ) /*No tendria ni por que poner un min-device, porq abarca todo lo humano...*/\t\t{\t\t}"; - var e = document.querySelector("script"); - e.parentNode.insertBefore(t, e); - var i = document.getElementById("captchacheckbox"), - n = i.dataset, - o = "true" === n.dark; - var a = document.createElement("div"); - a.className += " modal-captcha-container", a.innerHTML = '\t\t\t', document.getElementsByTagName("body")[0].appendChild(a); - var r = document.getElementsByClassName("modal-captcha-content").item(0); - document.getElementsByClassName("close-button-captcha").item(0).addEventListener("click", d), window.addEventListener("click", function(t) { - t.target === a && d() - }), i.addEventListener("change", function() { - if(this.checked){ - // console.log("checkbox checked"); - if (0 == ciframeLoaded) { - // console.log("n: ", n); - var t = ccreateIframeElement(n); - r.appendChild(t), ciframeLoaded = !0 - } - d() - } - }) - } +document.addEventListener('DOMContentLoaded', function () { + if (captchaStyleAdded) console.log('Captcha stuff already added!') + else { + console.log('Adding captcha stuff'), (captchaStyleAdded = !0) + var t = document.createElement('style') + t.innerHTML = + "\t/*Button*/\t\t.button-captcha-filled\t\t\t{\t\t\tdisplay: flex;\t\t\talign-items: center;\t\t\tjustify-content: center;\t\t\twidth: 120px;\t\t\tmin-width: 30px;\t\t\theight: 40px;\t\t\tline-height: 2.5;\t\t\ttext-align: center;\t\t\tcursor: pointer;\t\t\t/* Rectangle 2: */\t\t\tbackground: #FF7979;\t\t\tbox-shadow: 0 2px 4px 0 rgba(0,0,0,0.20);\t\t\tborder-radius: 20px;\t\t\t/* Sign up: */\t\t\tfont-family: 'Avenir-Heavy', Futura, Helvetica, Arial;\t\t\tfont-size: 16px;\t\t\tcolor: #FFFFFF;\t\t}\t\t.button-captcha-filled:hover\t\t{\t\t\tbackground:#FFFFFF;\t\t\tcolor: #FF7979;\t\t\tbox-shadow: 0 0 4px 0 rgba(0,0,0,0.20);\t\t}\t\t.button-captcha-filled:active\t\t{\t\t\tbackground:#FFFFFF;\t\t\tcolor: #FF7979;\t\t\t/*Move it down a little bit*/\t\t\tposition: relative;\t\t\ttop: 1px;\t\t}\t\t.button-captcha-filled-dark\t\t\t{\t\t\tdisplay: flex;\t\t\talign-items: center;\t\t\tjustify-content: center;\t\t\twidth: 120px;\t\t\tmin-width: 30px;\t\t\theight: 40px;\t\t\tline-height: 2.5;\t\t\ttext-align: center;\t\t\tcursor: pointer;\t\t\t/* Rectangle 2: */\t\t\tbackground: #161C38;\t\t\tbox-shadow: 0 0px 4px 0 rgba(0,0,0,0.20);\t\t\tborder-radius: 20px;\t\t\t/* Sign up: */\t\t\tfont-family: 'Avenir-Heavy', Futura, Helvetica, Arial;\t\t\tfont-size: 16px;\t\t\tcolor: #FFFFFF;\t\t}\t\t.button-captcha-filled-dark:hover\t\t{\t\t\tbackground:#FFFFFF;\t\t\tcolor: #161C38;\t\t\tbox-shadow: 0 0px 4px 0 rgba(0,0,0,0.20);\t\t}\t\t.button-captcha-filled-dark:active\t\t{\t\t\tbackground:#FFFFFF;\t\t\tcolor: #161C38;\t\t\t/*Move it down a little bit*/\t\t\tposition: relative;\t\t\ttop: 1px;\t\t}\t\t.modal-captcha-container {\t\t position: fixed;\t\t z-index: 1000;\t\t text-align: left;/*Si no añado esto, a veces hereda el text-align:center del body, y entonces el popup queda movido a la derecha, por center + margin left que aplico*/\t\t left: 0;\t\t top: 0;\t\t width: 100%;\t\t height: 100%;\t\t background-color: rgba(0, 0, 0, 0.5);\t\t opacity: 0;\t\t visibility: hidden;\t\t transform: scale(1.1);\t\t transition: visibility 0s linear 0.25s, opacity 0.25s 0s, transform 0.25s;\t\t}\t\t.modal-captcha-content {\t\t position: absolute;\t\t top: 50%;\t\t left: 50%;\t\t transform: translate(-50%, -50%);\t\t background-color: white;\t\t width: 100%;\t\t height: 100%;\t\t border-radius: 0.5rem;\t\t /*Rounded shadowed borders*/\t\t\tbox-shadow: 2px 2px 4px 0 rgba(0,0,0,0.15);\t\t\tborder-radius: 5px;\t\t}\t\t.close-button-captcha {\t\t float: right;\t\t width: 1.5rem;\t\t line-height: 1.5rem;\t\t text-align: center;\t\t cursor: pointer;\t\t margin-right:20px;\t\t margin-top:10px;\t\t border-radius: 0.25rem;\t\t background-color: lightgray;\t\t}\t\t.close-button-captcha:hover {\t\t background-color: darkgray;\t\t}\t\t.show-modal-captcha {\t\t opacity: 1;\t\t visibility: visible;\t\t transform: scale(1.0);\t\t transition: visibility 0s linear 0s, opacity 0.25s 0s, transform 0.25s;\t\t}\t\t/* Mobile */\t\t@media screen and (min-device-width: 160px) and ( max-width: 1077px ) /*No tendria ni por que poner un min-device, porq abarca todo lo humano...*/\t\t{\t\t}" + var e = document.querySelector('script') + e.parentNode.insertBefore(t, e) + var i = document.getElementById('captchacheckbox'), + n = i.dataset, + o = 'true' === n.dark + var a = document.createElement('div') + ;(a.className += ' modal-captcha-container'), + (a.innerHTML = + '\t\t\t'), + document.getElementsByTagName('body')[0].appendChild(a) + var r = document.getElementsByClassName('modal-captcha-content').item(0) + document + .getElementsByClassName('close-button-captcha') + .item(0) + .addEventListener('click', d), + window.addEventListener('click', function (t) { + t.target === a && d() + }), + i.addEventListener('change', function () { + if (this.checked) { + // console.log("checkbox checked"); + if (0 == ciframeLoaded) { + // console.log("n: ", n); + var t = ccreateIframeElement(n) + r.appendChild(t), (ciframeLoaded = !0) + } + d() + } + }) + } - function d() { - a.classList.toggle("show-modal-captcha") - } -}); + function d() { + a.classList.toggle('show-modal-captcha') + } +}) -function receiveMessage(event){ - if (event.data.includes("paymenthash")){ - // console.log("paymenthash received: ", event.data); - document.getElementById("captchapayhash").value = event.data.split("_")[1]; - } - if (event.data.includes("removetheiframe")){ - if (event.data.includes("nok")){ - //invoice was NOT paid - // console.log("receiveMessage not paid") - document.getElementById("captchacheckbox").checked = false; - } - ciframeLoaded = !1; - var element = document.getElementById('captcha-iframe'); - document.getElementsByClassName("modal-captcha-container")[0].classList.toggle("show-modal-captcha"); - element.parentNode.removeChild(element); - } +function receiveMessage(event) { + if (event.data.includes('paymenthash')) { + // console.log("paymenthash received: ", event.data); + document.getElementById('captchapayhash').value = event.data.split('_')[1] + } + if (event.data.includes('removetheiframe')) { + if (event.data.includes('nok')) { + //invoice was NOT paid + // console.log("receiveMessage not paid") + document.getElementById('captchacheckbox').checked = false + } + ciframeLoaded = !1 + var element = document.getElementById('captcha-iframe') + document + .getElementsByClassName('modal-captcha-container')[0] + .classList.toggle('show-modal-captcha') + element.parentNode.removeChild(element) + } } -window.addEventListener("message", receiveMessage, false); - - +window.addEventListener('message', receiveMessage, false) diff --git a/lnbits/extensions/captcha/templates/captcha/display.html b/lnbits/extensions/captcha/templates/captcha/display.html index af40ff4a..80e59e63 100644 --- a/lnbits/extensions/captcha/templates/captcha/display.html +++ b/lnbits/extensions/captcha/templates/captcha/display.html @@ -46,7 +46,11 @@ Copy invoice - Cancel @@ -58,7 +62,7 @@ Captcha accepted. You are probably human.

- diff --git a/lnbits/extensions/captcha/templates/captcha/index.html b/lnbits/extensions/captcha/templates/captcha/index.html index a83e1029..2250bced 100644 --- a/lnbits/extensions/captcha/templates/captcha/index.html +++ b/lnbits/extensions/captcha/templates/captcha/index.html @@ -106,7 +106,7 @@ label="Wallet *" > -\n' - + '\n' - + '
\n' - + '\n' - + '