From b8a5cc60b8ebedb54c96da4a79cc5fc600367fe5 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Thu, 2 Feb 2023 12:55:37 +0000 Subject: [PATCH 01/14] add pyright workflow, unify mypy+flake8+pylint workflows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: dni ⚡ --- .github/workflows/flake8.yml | 12 ++++++------ .github/workflows/mypy.yml | 4 ++-- .github/workflows/pylint.yml | 12 ++++++------ .github/workflows/pyright.yml | 28 ++++++++++++++++++++++++++++ 4 files changed, 42 insertions(+), 14 deletions(-) create mode 100644 .github/workflows/pyright.yml diff --git a/.github/workflows/flake8.yml b/.github/workflows/flake8.yml index 980b9c7b9..51bf1ae9c 100644 --- a/.github/workflows/flake8.yml +++ b/.github/workflows/flake8.yml @@ -10,18 +10,18 @@ jobs: python-version: ["3.9"] poetry-version: ["1.3.1"] steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} + - uses: actions/checkout@v3 - name: Set up Poetry ${{ matrix.poetry-version }} uses: abatilo/actions-poetry@v2 with: poetry-version: ${{ matrix.poetry-version }} + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + cache: "poetry" - name: Install dependencies run: | - poetry config virtualenvs.create false poetry install - name: Run tests run: make flake8 diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index ad5be69d6..1e168deca 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -19,9 +19,9 @@ jobs: uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - cache: 'poetry' + cache: "poetry" - name: Install dependencies run: | poetry install - name: Run tests - run: poetry run mypy + run: make mypy diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml index 8a43025b7..5c8eb29a0 100644 --- a/.github/workflows/pylint.yml +++ b/.github/workflows/pylint.yml @@ -10,18 +10,18 @@ jobs: python-version: ["3.9"] poetry-version: ["1.3.1"] steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} + - uses: actions/checkout@v3 - name: Set up Poetry ${{ matrix.poetry-version }} uses: abatilo/actions-poetry@v2 with: poetry-version: ${{ matrix.poetry-version }} + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + cache: "poetry" - name: Install dependencies run: | - poetry config virtualenvs.create false poetry install - name: Run tests run: make pylint diff --git a/.github/workflows/pyright.yml b/.github/workflows/pyright.yml new file mode 100644 index 000000000..8639cc769 --- /dev/null +++ b/.github/workflows/pyright.yml @@ -0,0 +1,28 @@ +name: pyright + +on: [push, pull_request] + +jobs: + check: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.9"] + poetry-version: ["1.3.1"] + steps: + - uses: actions/checkout@v3 + - name: Set up Poetry ${{ matrix.poetry-version }} + uses: abatilo/actions-poetry@v2 + with: + poetry-version: ${{ matrix.poetry-version }} + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + cache: "poetry" + - name: Install dependencies + run: | + poetry install + npm install + - name: Run tests + run: make pyright From 09eab69e12fa4848f00caa2239b42cc7ec9aa7c2 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Thu, 2 Feb 2023 12:56:10 +0000 Subject: [PATCH 02/14] fix pyright in Makefile, add pyright+pylint+flake8 to check target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: dni ⚡ --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 5bdbc9480..c3f4de567 100644 --- a/Makefile +++ b/Makefile @@ -4,13 +4,13 @@ all: format check requirements.txt format: prettier isort black -check: mypy checkprettier checkisort checkblack +check: mypy pyright checkprettier checkisort checkblack pylint flake8 prettier: $(shell find lnbits -name "*.js" -o -name ".html") ./node_modules/.bin/prettier --write lnbits/static/js/*.js lnbits/core/static/js/*.js lnbits/extensions/*/templates/*/*.html ./lnbits/core/templates/core/*.html lnbits/templates/*.html lnbits/extensions/*/static/js/*.js lnbits/extensions/*/static/components/*/*.js lnbits/extensions/*/static/components/*/*.html pyright: - ./node_modules/.bin/pyright + poetry run ./node_modules/.bin/pyright black: poetry run black . From 3bd68cb394afcccc28df27f4bf47a248ebf7131e Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Thu, 2 Feb 2023 12:57:52 +0000 Subject: [PATCH 03/14] fix pyright lnbits/extensions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: dni ⚡ --- lnbits/extensions/bleskomat/exchange_rates.py | 6 +-- lnbits/extensions/cashu/models.py | 7 ++-- lnbits/extensions/gerty/number_prefixer.py | 22 ++++------ lnbits/extensions/livestream/crud.py | 18 ++++----- lnbits/extensions/livestream/models.py | 13 ++++++ lnbits/extensions/lnaddress/views_api.py | 4 +- lnbits/extensions/lnurlp/models.py | 4 +- lnbits/extensions/market/notifier.py | 6 +-- lnbits/extensions/market/views.py | 27 +++---------- lnbits/extensions/market/views_api.py | 7 ++-- lnbits/extensions/ngrok/views.py | 2 +- lnbits/extensions/offlineshop/crud.py | 6 +-- lnbits/extensions/offlineshop/models.py | 6 ++- lnbits/extensions/satsdice/lnurl.py | 4 +- lnbits/extensions/satspay/crud.py | 6 ++- lnbits/extensions/splitpayments/crud.py | 2 +- lnbits/extensions/splitpayments/models.py | 7 +++- lnbits/extensions/streamalerts/crud.py | 13 ++++-- lnbits/extensions/subdomains/cloudflare.py | 40 +++++++------------ lnbits/extensions/subdomains/views_api.py | 24 +++++------ lnbits/extensions/tipjar/helpers.py | 20 ---------- lnbits/extensions/tipjar/views.py | 3 +- lnbits/extensions/tipjar/views_api.py | 28 ++++++++----- lnbits/extensions/watchonly/helpers.py | 9 ++++- lnbits/extensions/watchonly/views_api.py | 18 ++++----- 25 files changed, 142 insertions(+), 160 deletions(-) delete mode 100644 lnbits/extensions/tipjar/helpers.py diff --git a/lnbits/extensions/bleskomat/exchange_rates.py b/lnbits/extensions/bleskomat/exchange_rates.py index aff9ce657..b0a3969c7 100644 --- a/lnbits/extensions/bleskomat/exchange_rates.py +++ b/lnbits/extensions/bleskomat/exchange_rates.py @@ -80,7 +80,5 @@ async def fetch_fiat_exchange_rate(currency: str, provider: str): else: data = {} getter = exchange_rate_providers[provider]["getter"] - print(getter) - if callable(getter): - rate = float(getter(data, replacements)) - return rate + assert callable(getter), "cannot call getter function" + return float(getter(data, replacements)) diff --git a/lnbits/extensions/cashu/models.py b/lnbits/extensions/cashu/models.py index 84f28c2bc..aaff195f8 100644 --- a/lnbits/extensions/cashu/models.py +++ b/lnbits/extensions/cashu/models.py @@ -68,10 +68,11 @@ class Proof(BaseModel): def from_dict(cls, d: dict): assert "secret" in d, "no secret in proof" assert "amount" in d, "no amount in proof" + assert "C" in d, "no C in proof" return cls( - amount=d.get("amount"), - C=d.get("C"), - secret=d.get("secret"), + amount=d["amount"], + C=d["C"], + secret=d["secret"], reserved=d.get("reserved") or False, send_id=d.get("send_id") or "", time_created=d.get("time_created") or "", diff --git a/lnbits/extensions/gerty/number_prefixer.py b/lnbits/extensions/gerty/number_prefixer.py index dca001087..78f583bf4 100644 --- a/lnbits/extensions/gerty/number_prefixer.py +++ b/lnbits/extensions/gerty/number_prefixer.py @@ -1,7 +1,8 @@ import math +from typing import Tuple -def si_classifier(val): +def si_classifier(val) -> dict: suffixes = { 24: {"long_suffix": "yotta", "short_suffix": "Y", "scalar": 10**24}, 21: {"long_suffix": "zetta", "short_suffix": "Z", "scalar": 10**21}, @@ -22,24 +23,22 @@ def si_classifier(val): -24: {"long_suffix": "yocto", "short_suffix": "y", "scalar": 10**-24}, } exponent = int(math.floor(math.log10(abs(val)) / 3.0) * 3) - return suffixes.get(exponent, None) + suffix = suffixes.get(exponent) + assert suffix, f"could not classify: {val}" + return suffix -def si_formatter(value): +def si_formatter(value) -> Tuple: """ Return a triple of scaled value, short suffix, long suffix, or None if the value cannot be classified. """ classifier = si_classifier(value) - if classifier is None: - # Don't know how to classify this value - return None - scaled = value / classifier["scalar"] - return (scaled, classifier["short_suffix"], classifier["long_suffix"]) + return scaled, classifier["short_suffix"], classifier["long_suffix"] -def si_format(value, precision=4, long_form=False, separator=""): +def si_format(value: float, precision=4, long_form=False, separator="") -> str: """ "SI prefix" formatted string: return a string with the given precision and an appropriate order-of-3-magnitudes suffix, e.g.: @@ -47,11 +46,6 @@ def si_format(value, precision=4, long_form=False, separator=""): si_format(0.00000000123, long_form=True, separator=' ') => '1.230 nano' """ scaled, short_suffix, long_suffix = si_formatter(value) - - if scaled is None: - # Don't know how to format this value - return value - suffix = long_suffix if long_form else short_suffix if abs(scaled) < 10: diff --git a/lnbits/extensions/livestream/crud.py b/lnbits/extensions/livestream/crud.py index 4784494c0..fe4194eb0 100644 --- a/lnbits/extensions/livestream/crud.py +++ b/lnbits/extensions/livestream/crud.py @@ -23,14 +23,14 @@ async def create_livestream(*, wallet_id: str) -> int: if db.type == SQLITE: return result._result_proxy.lastrowid else: - return result[0] + return result[0] # type: ignore async def get_livestream(ls_id: int) -> Optional[Livestream]: row = await db.fetchone( "SELECT * FROM livestream.livestreams WHERE id = ?", (ls_id,) ) - return Livestream(**dict(row)) if row else None + return Livestream(**row) if row else None async def get_livestream_by_track(track_id: int) -> Optional[Livestream]: @@ -42,7 +42,7 @@ async def get_livestream_by_track(track_id: int) -> Optional[Livestream]: """, (track_id,), ) - return Livestream(**dict(row)) if row else None + return Livestream(**row) if row else None async def get_or_create_livestream_by_wallet(wallet: str) -> Optional[Livestream]: @@ -55,7 +55,7 @@ async def get_or_create_livestream_by_wallet(wallet: str) -> Optional[Livestream ls_id = await create_livestream(wallet_id=wallet) return await get_livestream(ls_id) - return Livestream(**dict(row)) if row else None + return Livestream(**row) if row else None async def update_current_track(ls_id: int, track_id: Optional[int]): @@ -121,7 +121,7 @@ async def get_track(track_id: Optional[int]) -> Optional[Track]: """, (track_id,), ) - return Track(**dict(row)) if row else None + return Track(**row) if row else None async def get_tracks(livestream: int) -> List[Track]: @@ -132,7 +132,7 @@ async def get_tracks(livestream: int) -> List[Track]: """, (livestream,), ) - return [Track(**dict(row)) for row in rows] + return [Track(**row) for row in rows] async def delete_track_from_livestream(livestream: int, track_id: int): @@ -174,7 +174,7 @@ async def add_producer(livestream: int, name: str) -> int: if db.type == SQLITE: return result._result_proxy.lastrowid else: - return result[0] + return result[0] # type: ignore async def get_producer(producer_id: int) -> Optional[Producer]: @@ -185,7 +185,7 @@ async def get_producer(producer_id: int) -> Optional[Producer]: """, (producer_id,), ) - return Producer(**dict(row)) if row else None + return Producer(**row) if row else None async def get_producers(livestream: int) -> List[Producer]: @@ -196,4 +196,4 @@ async def get_producers(livestream: int) -> List[Producer]: """, (livestream,), ) - return [Producer(**dict(row)) for row in rows] + return [Producer(**row) for row in rows] diff --git a/lnbits/extensions/livestream/models.py b/lnbits/extensions/livestream/models.py index 5d617da99..31d3f6ebf 100644 --- a/lnbits/extensions/livestream/models.py +++ b/lnbits/extensions/livestream/models.py @@ -1,4 +1,5 @@ import json +from sqlite3 import Row from typing import Optional from fastapi import Query, Request @@ -27,6 +28,10 @@ class Livestream(BaseModel): url = request.url_for("livestream.lnurl_livestream", ls_id=self.id) return lnurl_encode(url) + @classmethod + def from_row(cls, row: Row): + return cls(**dict(row)) + class Track(BaseModel): id: int @@ -35,6 +40,10 @@ class Track(BaseModel): name: str producer: int + @classmethod + def from_row(cls, row: Row): + return cls(**dict(row)) + @property def min_sendable(self) -> int: return min(100_000, self.price_msat or 100_000) @@ -88,3 +97,7 @@ class Producer(BaseModel): user: str wallet: str name: str + + @classmethod + def from_row(cls, row: Row): + return cls(**dict(row)) diff --git a/lnbits/extensions/lnaddress/views_api.py b/lnbits/extensions/lnaddress/views_api.py index 7d15a55f7..3b44fc31b 100644 --- a/lnbits/extensions/lnaddress/views_api.py +++ b/lnbits/extensions/lnaddress/views_api.py @@ -71,9 +71,7 @@ async def api_domain_create( if not cf_response or not cf_response["success"]: await delete_domain(domain.id) raise HTTPException( - status_code=HTTPStatus.BAD_REQUEST, - detail="Problem with cloudflare: " - + cf_response["errors"][0]["message"], + status_code=HTTPStatus.BAD_REQUEST, detail="Problem with cloudflare." ) return domain.dict() diff --git a/lnbits/extensions/lnurlp/models.py b/lnbits/extensions/lnurlp/models.py index 1c6b6f711..ce1095794 100644 --- a/lnbits/extensions/lnurlp/models.py +++ b/lnbits/extensions/lnurlp/models.py @@ -61,8 +61,8 @@ class PayLink(BaseModel): def success_action(self, payment_hash: str) -> Optional[Dict]: if self.success_url: url: ParseResult = urlparse(self.success_url) - qs: Dict = parse_qs(url.query) - qs["payment_hash"] = payment_hash + qs = parse_qs(url.query) + setattr(qs, "payment_hash", payment_hash) url = url._replace(query=urlencode(qs, doseq=True)) return { "tag": "url", diff --git a/lnbits/extensions/market/notifier.py b/lnbits/extensions/market/notifier.py index 88a1a4a38..4fe366f3e 100644 --- a/lnbits/extensions/market/notifier.py +++ b/lnbits/extensions/market/notifier.py @@ -6,6 +6,7 @@ and delivery to the specific person import json from collections import defaultdict +from typing import AsyncGenerator from fastapi import WebSocket from loguru import logger @@ -34,7 +35,7 @@ class Notifier: # Create notification generator: self.generator = self.get_notification_generator() - async def get_notification_generator(self): + async def get_notification_generator(self) -> AsyncGenerator: """Notification Generator""" while True: @@ -54,9 +55,8 @@ class Notifier: logger.exception(f"There is no member in room: {room_name}") return None - async def push(self, message: str, room_name: str = None): + async def push(self, message: str, room_name: str): """Push a message""" - message_body = {"message": message, "room_name": room_name} await self.generator.asend(message_body) diff --git a/lnbits/extensions/market/views.py b/lnbits/extensions/market/views.py index 0bcfac459..f9e7131bc 100644 --- a/lnbits/extensions/market/views.py +++ b/lnbits/extensions/market/views.py @@ -1,15 +1,8 @@ -import json from http import HTTPStatus -from fastapi import ( - BackgroundTasks, - Depends, - Query, - Request, - WebSocket, - WebSocketDisconnect, -) +from fastapi import Depends, Query, Request, WebSocket, WebSocketDisconnect from fastapi.templating import Jinja2Templates +from loguru import logger from starlette.exceptions import HTTPException from starlette.responses import HTMLResponse @@ -147,24 +140,14 @@ notifier = Notifier() @market_ext.websocket("/ws/{room_name}") -async def websocket_endpoint( - websocket: WebSocket, room_name: str, background_tasks: BackgroundTasks -): +async def websocket_endpoint(websocket: WebSocket, room_name: str): await notifier.connect(websocket, room_name) try: while True: data = await websocket.receive_text() - d = json.loads(data) - d["room_name"] = room_name - - room_members = ( - notifier.get_members(room_name) - if notifier.get_members(room_name) is not None - else [] - ) - + room_members = notifier.get_members(room_name) or [] if websocket not in room_members: - print("Sender not in room member: Reconnecting...") + logger.warning("Sender not in room member: Reconnecting...") await notifier.connect(websocket, room_name) await notifier._notify(data, room_name) diff --git a/lnbits/extensions/market/views_api.py b/lnbits/extensions/market/views_api.py index ad0cbb463..221d51bbf 100644 --- a/lnbits/extensions/market/views_api.py +++ b/lnbits/extensions/market/views_api.py @@ -1,4 +1,5 @@ from http import HTTPStatus +from typing import Optional from fastapi import Depends, Query from loguru import logger @@ -224,7 +225,7 @@ async def api_market_stalls( @market_ext.put("/api/v1/stalls/{stall_id}") async def api_market_stall_create( data: createStalls, - stall_id: str = None, + stall_id: Optional[str] = None, wallet: WalletTypeInfo = Depends(require_invoice_key), ): @@ -447,7 +448,7 @@ async def api_market_market_stalls(market_id: str): @market_ext.put("/api/v1/markets/{market_id}") async def api_market_market_create( data: CreateMarket, - market_id: str = None, + market_id: Optional[str] = None, wallet: WalletTypeInfo = Depends(require_invoice_key), ): if market_id: @@ -506,7 +507,7 @@ async def api_get_settings(wallet: WalletTypeInfo = Depends(require_admin_key)): @market_ext.put("/api/v1/settings/{usr}") async def api_set_settings( data: SetSettings, - usr: str = None, + usr: Optional[str] = None, wallet: WalletTypeInfo = Depends(require_admin_key), ): if usr: diff --git a/lnbits/extensions/ngrok/views.py b/lnbits/extensions/ngrok/views.py index d84ecd2d9..2fa4df9cf 100644 --- a/lnbits/extensions/ngrok/views.py +++ b/lnbits/extensions/ngrok/views.py @@ -2,7 +2,7 @@ from os import getenv from fastapi import Depends, Request from fastapi.templating import Jinja2Templates -from pyngrok import conf, ngrok +from pyngrok import conf, ngrok # type: ignore from lnbits.core.models import User from lnbits.decorators import check_user_exists diff --git a/lnbits/extensions/offlineshop/crud.py b/lnbits/extensions/offlineshop/crud.py index 896842d80..1fa63f3e0 100644 --- a/lnbits/extensions/offlineshop/crud.py +++ b/lnbits/extensions/offlineshop/crud.py @@ -22,12 +22,12 @@ async def create_shop(*, wallet_id: str) -> int: if db.type == SQLITE: return result._result_proxy.lastrowid else: - return result[0] + return result[0] # type: ignore async def get_shop(id: int) -> Optional[Shop]: row = await db.fetchone("SELECT * FROM offlineshop.shops WHERE id = ?", (id,)) - return Shop(**dict(row)) if row else None + return Shop(**row) if row else None async def get_or_create_shop_by_wallet(wallet: str) -> Optional[Shop]: @@ -40,7 +40,7 @@ async def get_or_create_shop_by_wallet(wallet: str) -> Optional[Shop]: ls_id = await create_shop(wallet_id=wallet) return await get_shop(ls_id) - return Shop(**dict(row)) if row else None + return Shop(**row) if row else None async def set_method(shop: int, method: str, wordlist: str = "") -> Optional[Shop]: diff --git a/lnbits/extensions/offlineshop/models.py b/lnbits/extensions/offlineshop/models.py index d2e3b3d27..01044cb0f 100644 --- a/lnbits/extensions/offlineshop/models.py +++ b/lnbits/extensions/offlineshop/models.py @@ -52,7 +52,7 @@ class ShopCounter: # cleanup confirmation words cache to_remove = len(self.fulfilled_payments) - 23 if to_remove > 0: - for i in range(to_remove): + for _ in range(to_remove): self.fulfilled_payments.popitem(False) return word @@ -64,6 +64,10 @@ class Shop(BaseModel): method: str wordlist: str + @classmethod + def from_row(cls, row: Row): + return cls(**dict(row)) + @property def otp_key(self) -> str: return base64.b32encode( diff --git a/lnbits/extensions/satsdice/lnurl.py b/lnbits/extensions/satsdice/lnurl.py index f766d8cbd..2bb590162 100644 --- a/lnbits/extensions/satsdice/lnurl.py +++ b/lnbits/extensions/satsdice/lnurl.py @@ -18,8 +18,6 @@ from .crud import ( ) from .models import CreateSatsDicePayment -##############LNURLP STUFF - @satsdice_ext.get( "/api/v1/lnurlp/{link_id}", @@ -84,7 +82,7 @@ async def api_lnurlp_callback( data = CreateSatsDicePayment( satsdice_pay=link.id, - value=amount_received / 1000, + value=int(amount_received / 1000), payment_hash=payment_hash, ) diff --git a/lnbits/extensions/satspay/crud.py b/lnbits/extensions/satspay/crud.py index 01abe24e4..c13d0a4b8 100644 --- a/lnbits/extensions/satspay/crud.py +++ b/lnbits/extensions/satspay/crud.py @@ -13,7 +13,7 @@ from .helpers import fetch_onchain_balance from .models import Charges, CreateCharge, SatsPayThemes -async def create_charge(user: str, data: CreateCharge) -> Optional[Charges]: +async def create_charge(user: str, data: CreateCharge) -> Charges: data = CreateCharge(**data.dict()) charge_id = urlsafe_short_hash() if data.onchainwallet: @@ -79,7 +79,9 @@ async def create_charge(user: str, data: CreateCharge) -> Optional[Charges]: data.custom_css, ), ) - return await get_charge(charge_id) + charge = await get_charge(charge_id) + assert charge, "Newly created charge does not exist" + return charge async def update_charge(charge_id: str, **kwargs) -> Optional[Charges]: diff --git a/lnbits/extensions/splitpayments/crud.py b/lnbits/extensions/splitpayments/crud.py index de4e0822a..737e7bb9a 100644 --- a/lnbits/extensions/splitpayments/crud.py +++ b/lnbits/extensions/splitpayments/crud.py @@ -10,7 +10,7 @@ async def get_targets(source_wallet: str) -> List[Target]: rows = await db.fetchall( "SELECT * FROM splitpayments.targets WHERE source = ?", (source_wallet,) ) - return [Target(**dict(row)) for row in rows] + return [Target(**row) for row in rows] async def set_targets(source_wallet: str, targets: List[Target]): diff --git a/lnbits/extensions/splitpayments/models.py b/lnbits/extensions/splitpayments/models.py index fc3db2c6d..4f2bb0106 100644 --- a/lnbits/extensions/splitpayments/models.py +++ b/lnbits/extensions/splitpayments/models.py @@ -1,6 +1,7 @@ +from sqlite3 import Row from typing import List, Optional -from fastapi.param_functions import Query +from fastapi import Query from pydantic import BaseModel @@ -11,6 +12,10 @@ class Target(BaseModel): tag: str alias: Optional[str] + @classmethod + def from_row(cls, row: Row): + return cls(**dict(row)) + class TargetPutList(BaseModel): wallet: str = Query(...) diff --git a/lnbits/extensions/streamalerts/crud.py b/lnbits/extensions/streamalerts/crud.py index f376841a4..1745623d4 100644 --- a/lnbits/extensions/streamalerts/crud.py +++ b/lnbits/extensions/streamalerts/crud.py @@ -147,14 +147,16 @@ async def create_service(data: CreateService) -> Service: if db.type == SQLITE: service_id = result._result_proxy.lastrowid else: - service_id = result[0] + service_id = result[0] # type: ignore service = await get_service(service_id) assert service return service -async def get_service(service_id: int, by_state: str = None) -> Optional[Service]: +async def get_service( + service_id: int, by_state: Optional[str] = None +) -> Optional[Service]: """Return a service either by ID or, available, by state Each Service's donation page is reached through its "state" hash @@ -184,7 +186,9 @@ async def authenticate_service(service_id, code, redirect_uri): """Use authentication code from third party API to retreive access token""" # The API token is passed in the querystring as 'code' service = await get_service(service_id) + assert service wallet = await get_wallet(service.wallet) + assert wallet user = wallet.user url = "https://streamlabs.com/api/v1.0/token" data = { @@ -208,8 +212,11 @@ async def service_add_token(service_id, token): is not overwritten. Tokens for Streamlabs never need to be refreshed. """ - if (await get_service(service_id)).authenticated: + service = await get_service(service_id) + assert service + if service.authenticated: return False + await db.execute( "UPDATE streamalerts.Services SET authenticated = 1, token = ? where id = ?", (token, service_id), diff --git a/lnbits/extensions/subdomains/cloudflare.py b/lnbits/extensions/subdomains/cloudflare.py index d0d8c4f31..3d3b9bdeb 100644 --- a/lnbits/extensions/subdomains/cloudflare.py +++ b/lnbits/extensions/subdomains/cloudflare.py @@ -1,5 +1,3 @@ -import json - import httpx from .models import Domains @@ -20,25 +18,21 @@ async def cloudflare_create_subdomain( "Content-Type": "application/json", } aRecord = subdomain + "." + domain.domain - cf_response = "" async with httpx.AsyncClient() as client: - try: - r = await client.post( - url, - headers=header, - json={ - "type": record_type, - "name": aRecord, - "content": ip, - "ttl": 0, - "proxied": False, - }, - timeout=40, - ) - cf_response = json.loads(r.text) - except AssertionError: - cf_response = "Error occured" - return cf_response + r = await client.post( + url, + headers=header, + json={ + "type": record_type, + "name": aRecord, + "content": ip, + "ttl": 0, + "proxied": False, + }, + timeout=40, + ) + r.raise_for_status() + return r.json() async def cloudflare_deletesubdomain(domain: Domains, domain_id: str): @@ -52,8 +46,4 @@ async def cloudflare_deletesubdomain(domain: Domains, domain_id: str): "Content-Type": "application/json", } async with httpx.AsyncClient() as client: - try: - r = await client.delete(url + "/" + domain_id, headers=header, timeout=40) - r.text - except AssertionError: - pass + await client.delete(url + "/" + domain_id, headers=header, timeout=40) diff --git a/lnbits/extensions/subdomains/views_api.py b/lnbits/extensions/subdomains/views_api.py index 6f85c66e0..3c0330f53 100644 --- a/lnbits/extensions/subdomains/views_api.py +++ b/lnbits/extensions/subdomains/views_api.py @@ -121,21 +121,21 @@ async def api_subdomain_make_subdomain(domain_id, data: CreateSubdomain): detail=f"{data.subdomain}.{domain.domain} domain already taken.", ) - ## Dry run cloudflare... (create and if create is sucessful delete it) - cf_response = await cloudflare_create_subdomain( - domain=domain, - subdomain=data.subdomain, - record_type=data.record_type, - ip=data.ip, - ) - if cf_response["success"] is True: - await cloudflare_deletesubdomain( - domain=domain, domain_id=cf_response["result"]["id"] + ## Dry run cloudflare... (create and if create is successful delete it) + try: + res_json = await cloudflare_create_subdomain( + domain=domain, + subdomain=data.subdomain, + record_type=data.record_type, + ip=data.ip, ) - else: + await cloudflare_deletesubdomain( + domain=domain, domain_id=res_json["result"]["id"] + ) + except: raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, - detail=f'Problem with cloudflare: {cf_response["errors"][0]["message"]}', + detail="Problem with cloudflare.", ) ## ALL OK - create an invoice and return it to the user diff --git a/lnbits/extensions/tipjar/helpers.py b/lnbits/extensions/tipjar/helpers.py deleted file mode 100644 index 7214e19c3..000000000 --- a/lnbits/extensions/tipjar/helpers.py +++ /dev/null @@ -1,20 +0,0 @@ -from lnbits.core.crud import get_wallet - -from .crud import get_tipjar - - -async def get_charge_details(tipjar_id): - """Return the default details for a satspay charge""" - tipjar = await get_tipjar(tipjar_id) - wallet_id = tipjar.wallet - wallet = await get_wallet(wallet_id) - user = wallet.user - details = { - "time": 1440, - "user": user, - "lnbitswallet": wallet_id, - "onchainwallet": tipjar.onchain, - "completelink": "/tipjar/" + str(tipjar_id), - "completelinktext": "Thanks for the tip!", - } - return details diff --git a/lnbits/extensions/tipjar/views.py b/lnbits/extensions/tipjar/views.py index 56f718e21..ddb1b63c0 100644 --- a/lnbits/extensions/tipjar/views.py +++ b/lnbits/extensions/tipjar/views.py @@ -1,7 +1,6 @@ from http import HTTPStatus -from fastapi import Depends, Request -from fastapi.param_functions import Query +from fastapi import Depends, Query, Request from fastapi.templating import Jinja2Templates from starlette.exceptions import HTTPException diff --git a/lnbits/extensions/tipjar/views_api.py b/lnbits/extensions/tipjar/views_api.py index 7d3df9205..7d420fae8 100644 --- a/lnbits/extensions/tipjar/views_api.py +++ b/lnbits/extensions/tipjar/views_api.py @@ -3,7 +3,7 @@ from http import HTTPStatus from fastapi import Depends, Query from starlette.exceptions import HTTPException -from lnbits.core.crud import get_user +from lnbits.core.crud import get_user, get_wallet from lnbits.decorators import WalletTypeInfo, get_key_type # todo: use the API, not direct import @@ -22,7 +22,6 @@ from .crud import ( update_tip, update_tipjar, ) -from .helpers import get_charge_details from .models import createTip, createTipJar, createTips @@ -55,25 +54,32 @@ async def api_create_tip(data: createTips): status_code=HTTPStatus.NOT_FOUND, detail="Tipjar does not exist." ) - webhook = tipjar.webhook - charge_details = await get_charge_details(tipjar.id) + wallet_id = tipjar.wallet + wallet = await get_wallet(wallet_id) + if not wallet: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, detail="Tipjar wallet does not exist." + ) + name = data.name + # Ensure that description string can be split reliably name = name.replace('"', "''") if not name: name = "Anonymous" + description = f"{name}: {message}" charge = await create_charge( - user=charge_details["user"], + user=wallet.user, data=CreateCharge( amount=sats, - webhook=webhook or "", + webhook=tipjar.webhook or "", description=description, - onchainwallet=charge_details["onchainwallet"], - lnbitswallet=charge_details["lnbitswallet"], - completelink=charge_details["completelink"], - completelinktext=charge_details["completelinktext"], - time=charge_details["time"], + onchainwallet=tipjar.onchain or "", + lnbitswallet=tipjar.wallet, + completelink="/tipjar/" + str(tipjar_id), + completelinktext="Thanks for the tip!", + time=1440, custom_css="", ), ) diff --git a/lnbits/extensions/watchonly/helpers.py b/lnbits/extensions/watchonly/helpers.py index 8db9ff573..40e9788f6 100644 --- a/lnbits/extensions/watchonly/helpers.py +++ b/lnbits/extensions/watchonly/helpers.py @@ -1,3 +1,5 @@ +from typing import Optional, Tuple + from embit.descriptor import Descriptor, Key from embit.descriptor.arguments import AllowedDerivation from embit.networks import NETWORKS @@ -12,7 +14,7 @@ def detect_network(k): return net -def parse_key(masterpub: str) -> Descriptor: +def parse_key(masterpub: str) -> Tuple[Descriptor, Optional[dict]]: """Parses masterpub or descriptor and returns a tuple: (Descriptor, network) To create addresses use descriptor.derive(num).address(network=network) """ @@ -34,6 +36,7 @@ def parse_key(masterpub: str) -> Descriptor: k.allowed_derivation = AllowedDerivation.default() # get version bytes version = k.key.version + desc = Descriptor() for network_name in NETWORKS: net = NETWORKS[network_name] # not found in this network @@ -47,8 +50,9 @@ def parse_key(masterpub: str) -> Descriptor: desc = Descriptor.from_string("wpkh(%s)" % str(k)) break # we didn't find correct version - if network is None: + if not network: raise ValueError("Unknown master public key version") + else: desc = Descriptor.from_string(masterpub) if not desc.is_wildcard: @@ -61,6 +65,7 @@ def parse_key(masterpub: str) -> Descriptor: if network is not None and network != net: raise ValueError("Keys from different networks") network = net + return desc, network diff --git a/lnbits/extensions/watchonly/views_api.py b/lnbits/extensions/watchonly/views_api.py index 2e3fc45d8..e0c427fe7 100644 --- a/lnbits/extensions/watchonly/views_api.py +++ b/lnbits/extensions/watchonly/views_api.py @@ -73,7 +73,8 @@ async def api_wallet_create_or_update( data: CreateWallet, w: WalletTypeInfo = Depends(require_admin_key) ): try: - (descriptor, network) = parse_key(data.masterpub) + descriptor, network = parse_key(data.masterpub) + assert network if data.network != network["name"]: raise ValueError( "Account network error. This account is for '{}'".format( @@ -308,12 +309,9 @@ async def api_psbt_utxos_tx( raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=str(e)) -@watchonly_ext.put("/api/v1/psbt/extract") -async def api_psbt_extract_tx( - data: ExtractPsbt, w: WalletTypeInfo = Depends(require_admin_key) -): +@watchonly_ext.put("/api/v1/psbt/extract", dependencies=[Depends(require_admin_key)]) +async def api_psbt_extract_tx(data: ExtractPsbt): network = NETWORKS["main"] if data.network == "Mainnet" else NETWORKS["test"] - res = SignedTransaction() try: psbt = PSBT.from_base64(data.psbtBase64) for i, inp in enumerate(data.inputs): @@ -322,9 +320,9 @@ async def api_psbt_extract_tx( final_psbt = finalizer.finalize_psbt(psbt) if not final_psbt: raise ValueError("PSBT cannot be finalized!") - res.tx_hex = final_psbt.to_string() - transaction = Transaction.from_string(res.tx_hex) + tx_hex = final_psbt.to_string() + transaction = Transaction.from_string(tx_hex) tx = { "locktime": transaction.locktime, "version": transaction.version, @@ -336,10 +334,10 @@ async def api_psbt_extract_tx( tx["outputs"].append( {"amount": out.value, "address": out.script_pubkey.address(network)} ) - res.tx_json = json.dumps(tx) + signed_tx = SignedTransaction(tx_hex=tx_hex, tx_json=json.dumps(tx)) + return signed_tx.dict() except Exception as e: raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=str(e)) - return res.dict() @watchonly_ext.post("/api/v1/tx") From 16679c0d65770ee31124f7b328cc273d36ef5286 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Mon, 6 Feb 2023 13:24:32 +0100 Subject: [PATCH 04/14] exclude core from pyright --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 84104684d..847672fc2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -94,6 +94,9 @@ include = [ ] exclude = [ "lnbits/wallets/lnd_grpc_files", + "lnbits/wallets", + "lnbits/core", + "lnbits/*.py", ] [tool.mypy] From 05bb502bf56af109da2e8200ade0dbcc484791ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Tue, 7 Feb 2023 08:35:48 +0100 Subject: [PATCH 05/14] fix watchonly --- lnbits/extensions/watchonly/helpers.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lnbits/extensions/watchonly/helpers.py b/lnbits/extensions/watchonly/helpers.py index 40e9788f6..0ac364541 100644 --- a/lnbits/extensions/watchonly/helpers.py +++ b/lnbits/extensions/watchonly/helpers.py @@ -19,6 +19,7 @@ def parse_key(masterpub: str) -> Tuple[Descriptor, Optional[dict]]: To create addresses use descriptor.derive(num).address(network=network) """ network = None + desc = None # probably a single key if "(" not in masterpub: k = Key.from_string(masterpub) @@ -36,7 +37,6 @@ def parse_key(masterpub: str) -> Tuple[Descriptor, Optional[dict]]: k.allowed_derivation = AllowedDerivation.default() # get version bytes version = k.key.version - desc = Descriptor() for network_name in NETWORKS: net = NETWORKS[network_name] # not found in this network @@ -52,6 +52,8 @@ def parse_key(masterpub: str) -> Tuple[Descriptor, Optional[dict]]: # we didn't find correct version if not network: raise ValueError("Unknown master public key version") + if not desc: + raise ValueError("descriptor not found, because version did not match") else: desc = Descriptor.from_string(masterpub) From 8bf410352331e46bf99a7cebc002159db668756e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Tue, 7 Feb 2023 08:55:52 +0100 Subject: [PATCH 06/14] order of make check --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c3f4de567..636f8c337 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ all: format check requirements.txt format: prettier isort black -check: mypy pyright checkprettier checkisort checkblack pylint flake8 +check: mypy pyright pylint flake8 checkisort checkblack checkprettier prettier: $(shell find lnbits -name "*.js" -o -name ".html") ./node_modules/.bin/prettier --write lnbits/static/js/*.js lnbits/core/static/js/*.js lnbits/extensions/*/templates/*/*.html ./lnbits/core/templates/core/*.html lnbits/templates/*.html lnbits/extensions/*/static/js/*.js lnbits/extensions/*/static/components/*/*.js lnbits/extensions/*/static/components/*/*.html From f547b1f141f0811ddace3f84854c42ed03a305d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Tue, 7 Feb 2023 09:20:05 +0100 Subject: [PATCH 07/14] add assert message for streamalerts crud --- lnbits/extensions/streamalerts/crud.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lnbits/extensions/streamalerts/crud.py b/lnbits/extensions/streamalerts/crud.py index 1745623d4..55618a1a8 100644 --- a/lnbits/extensions/streamalerts/crud.py +++ b/lnbits/extensions/streamalerts/crud.py @@ -26,11 +26,11 @@ async def get_charge_details(service_id): These might be different depending for services implemented in the future. """ service = await get_service(service_id) - assert service + assert service, f"Could not fetch service: {service_id}" wallet_id = service.wallet wallet = await get_wallet(wallet_id) - assert wallet + assert wallet, f"Could not fetch wallet: {wallet_id}" user = wallet.user return { @@ -150,7 +150,7 @@ async def create_service(data: CreateService) -> Service: service_id = result[0] # type: ignore service = await get_service(service_id) - assert service + assert service, f"Could not fetch service: {service_id}" return service @@ -186,9 +186,9 @@ async def authenticate_service(service_id, code, redirect_uri): """Use authentication code from third party API to retreive access token""" # The API token is passed in the querystring as 'code' service = await get_service(service_id) - assert service + assert service, f"Could not fetch service: {service_id}" wallet = await get_wallet(service.wallet) - assert wallet + assert wallet, f"Could not fetch wallet: {service.wallet}" user = wallet.user url = "https://streamlabs.com/api/v1.0/token" data = { @@ -213,7 +213,7 @@ async def service_add_token(service_id, token): Tokens for Streamlabs never need to be refreshed. """ service = await get_service(service_id) - assert service + assert service, f"Could not fetch service: {service_id}" if service.authenticated: return False From e0d9f2d764dc69e58bdbfa9f6327da10cabd7681 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Tue, 7 Feb 2023 09:48:11 +0100 Subject: [PATCH 08/14] fixes for vlad :) --- lnbits/extensions/bleskomat/exchange_rates.py | 3 ++- lnbits/extensions/subdomains/views_api.py | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lnbits/extensions/bleskomat/exchange_rates.py b/lnbits/extensions/bleskomat/exchange_rates.py index b0a3969c7..c316e7e3b 100644 --- a/lnbits/extensions/bleskomat/exchange_rates.py +++ b/lnbits/extensions/bleskomat/exchange_rates.py @@ -80,5 +80,6 @@ async def fetch_fiat_exchange_rate(currency: str, provider: str): else: data = {} getter = exchange_rate_providers[provider]["getter"] - assert callable(getter), "cannot call getter function" + if not callable(getter): + return None return float(getter(data, replacements)) diff --git a/lnbits/extensions/subdomains/views_api.py b/lnbits/extensions/subdomains/views_api.py index 3c0330f53..5bbd8517a 100644 --- a/lnbits/extensions/subdomains/views_api.py +++ b/lnbits/extensions/subdomains/views_api.py @@ -1,6 +1,7 @@ from http import HTTPStatus from fastapi import Depends, Query +from loguru import logger from starlette.exceptions import HTTPException from lnbits.core.crud import get_user @@ -132,7 +133,8 @@ async def api_subdomain_make_subdomain(domain_id, data: CreateSubdomain): await cloudflare_deletesubdomain( domain=domain, domain_id=res_json["result"]["id"] ) - except: + except Exception as exc: + logger.warning(exc) raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail="Problem with cloudflare.", From 2fb8eb505a45fa20558279e03c239f5d2c3d2886 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Tue, 7 Feb 2023 10:11:50 +0100 Subject: [PATCH 09/14] try catch subdomain invoice_listener --- lnbits/extensions/subdomains/tasks.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/lnbits/extensions/subdomains/tasks.py b/lnbits/extensions/subdomains/tasks.py index aef4b49b1..fbbac88fb 100644 --- a/lnbits/extensions/subdomains/tasks.py +++ b/lnbits/extensions/subdomains/tasks.py @@ -1,7 +1,8 @@ import asyncio - import httpx +from loguru import logger + from lnbits.core.models import Payment from lnbits.helpers import get_current_extension_name from lnbits.tasks import register_invoice_listener @@ -21,7 +22,7 @@ async def wait_for_paid_invoices(): async def on_invoice_paid(payment: Payment) -> None: if payment.extra.get("tag") != "lnsubdomain": - # not an lnurlp invoice + # not an lnsubdomain invoice return await payment.set_pending(False) @@ -29,12 +30,19 @@ async def on_invoice_paid(payment: Payment) -> None: domain = await get_domain(subdomain.domain) ### Create subdomain - cf_response = await cloudflare_create_subdomain( - domain=domain, # type: ignore - subdomain=subdomain.subdomain, - record_type=subdomain.record_type, - ip=subdomain.ip, - ) + try: + cf_response = await cloudflare_create_subdomain( + domain=domain, # type: ignore + subdomain=subdomain.subdomain, + record_type=subdomain.record_type, + ip=subdomain.ip, + ) + except Exception as exc: + logger.error(exc) + logger.error("could not create subdomain on cloudflare") + return + + ### Use webhook to notify about cloudflare registration if domain and domain.webhook: From 9f6dc18be7d7cdcae0d8cf9619035b7456d78121 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Tue, 7 Feb 2023 10:17:01 +0100 Subject: [PATCH 10/14] subdomain formatting --- lnbits/extensions/subdomains/tasks.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lnbits/extensions/subdomains/tasks.py b/lnbits/extensions/subdomains/tasks.py index fbbac88fb..42b7885a1 100644 --- a/lnbits/extensions/subdomains/tasks.py +++ b/lnbits/extensions/subdomains/tasks.py @@ -1,6 +1,6 @@ import asyncio -import httpx +import httpx from loguru import logger from lnbits.core.models import Payment @@ -42,8 +42,6 @@ async def on_invoice_paid(payment: Payment) -> None: logger.error("could not create subdomain on cloudflare") return - - ### Use webhook to notify about cloudflare registration if domain and domain.webhook: async with httpx.AsyncClient() as client: From 538f44e6f0bd0db10fb12d1f4b80eb93dd4361e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Tue, 7 Feb 2023 10:31:05 +0100 Subject: [PATCH 11/14] lnadress, fixes for vlad :) --- lnbits/extensions/lnaddress/views_api.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lnbits/extensions/lnaddress/views_api.py b/lnbits/extensions/lnaddress/views_api.py index 3b44fc31b..cdf5b91fb 100644 --- a/lnbits/extensions/lnaddress/views_api.py +++ b/lnbits/extensions/lnaddress/views_api.py @@ -2,6 +2,7 @@ from http import HTTPStatus from urllib.parse import urlparse from fastapi import Depends, HTTPException, Query, Request +from loguru import logger from lnbits.core.crud import get_user from lnbits.core.services import check_transaction_status, create_invoice @@ -70,6 +71,7 @@ async def api_domain_create( if not cf_response or not cf_response["success"]: await delete_domain(domain.id) + logger.error("Cloudflare failed with: " + cf_response["errors"][0]["message"]) # type: ignore raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail="Problem with cloudflare." ) From 96a66376bda5cf6e4267b1481d7069e99f3017fc Mon Sep 17 00:00:00 2001 From: ben Date: Tue, 7 Feb 2023 11:46:17 +0000 Subject: [PATCH 12/14] Cowboy fix for lnurlpay success action Pyment hash is still sent https://webhook.site/#!/8e4e4541-0ea3-4ede-8765-68ff261f5c97/ff4136b6-220d-4b35-b865-5a19fa924947/1, so these lines are prob not needed --- lnbits/extensions/lnurlp/models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lnbits/extensions/lnurlp/models.py b/lnbits/extensions/lnurlp/models.py index ce1095794..dc715b777 100644 --- a/lnbits/extensions/lnurlp/models.py +++ b/lnbits/extensions/lnurlp/models.py @@ -61,9 +61,9 @@ class PayLink(BaseModel): def success_action(self, payment_hash: str) -> Optional[Dict]: if self.success_url: url: ParseResult = urlparse(self.success_url) - qs = parse_qs(url.query) - setattr(qs, "payment_hash", payment_hash) - url = url._replace(query=urlencode(qs, doseq=True)) + #qs = parse_qs(url.query) + #setattr(qs, "payment_hash", payment_hash) + #url = url._replace(query=urlencode(qs, doseq=True)) return { "tag": "url", "description": self.success_text or "~", From eb54d6fe9217003a993d23e40851d29cae6dc907 Mon Sep 17 00:00:00 2001 From: ben Date: Tue, 7 Feb 2023 14:47:03 +0000 Subject: [PATCH 13/14] Less fancy, but working db call --- lnbits/extensions/livestream/crud.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lnbits/extensions/livestream/crud.py b/lnbits/extensions/livestream/crud.py index fe4194eb0..037851aa2 100644 --- a/lnbits/extensions/livestream/crud.py +++ b/lnbits/extensions/livestream/crud.py @@ -36,13 +36,17 @@ async def get_livestream(ls_id: int) -> Optional[Livestream]: async def get_livestream_by_track(track_id: int) -> Optional[Livestream]: row = await db.fetchone( """ - SELECT livestreams.* AS livestreams FROM livestream.livestreams - INNER JOIN livestream.tracks AS tracks ON tracks.livestream = livestreams.id - WHERE tracks.id = ? + SELECT * FROM livestream.tracks WHERE tracks.id = ? """, (track_id,), ) - return Livestream(**row) if row else None + row2 = await db.fetchone( + """ + SELECT * FROM livestream.livestreams WHERE livestreams.id = ? + """, + (row.livestream,), + ) + return Livestream(**row2) if row2 else None async def get_or_create_livestream_by_wallet(wallet: str) -> Optional[Livestream]: From c9e027ca21038cac20599d6182ecaa5fee68c123 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Wed, 8 Feb 2023 08:35:46 +0100 Subject: [PATCH 14/14] removed unused deps --- lnbits/extensions/lnurlp/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lnbits/extensions/lnurlp/models.py b/lnbits/extensions/lnurlp/models.py index dc715b777..4ee82aad5 100644 --- a/lnbits/extensions/lnurlp/models.py +++ b/lnbits/extensions/lnurlp/models.py @@ -1,7 +1,7 @@ import json from sqlite3 import Row from typing import Dict, Optional -from urllib.parse import ParseResult, parse_qs, urlencode, urlparse, urlunparse +from urllib.parse import ParseResult, urlparse, urlunparse from fastapi.param_functions import Query from lnurl.types import LnurlPayMetadata