From 5a96bcd558ef9e1496806dc127283c4a4ea89300 Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 23 Nov 2022 21:47:26 +0000 Subject: [PATCH 01/19] Adds universal websocket manager any extension can use Connect to the `ws:///api/v1/ws/{item_id}` endpoint POST data to the websocket with `https:///api/v1/ws/{item_id}` --- lnbits/core/views/api.py | 46 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 983d5a261..8f7c11a2e 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -12,7 +12,7 @@ from urllib.parse import ParseResult, parse_qs, urlencode, urlparse, urlunparse import async_timeout import httpx import pyqrcode -from fastapi import Depends, Header, Query, Request +from fastapi import Depends, Header, Query, Request, WebSocket, WebSocketDisconnect from fastapi.exceptions import HTTPException from fastapi.params import Body from loguru import logger @@ -697,3 +697,47 @@ async def api_auditor(wallet: WalletTypeInfo = Depends(get_key_type)): "delta_msats": delta, "timestamp": int(time.time()), } + + +##################UNIVERSAL WEBSOCKET MANAGER######################## + +class websocketConnectionManager: + def __init__(self): + self.active_connections: List[WebSocket] = [] + + async def connect(self, websocket: WebSocket, item_id: str): + await websocket.accept() + websocket.id = item_id + self.active_connections.append(websocket) + + def disconnect(self, websocket: WebSocket): + self.active_connections.remove(websocket) + + async def send_personal_message(self, message: str, item_id: str): + for connection in self.active_connections: + if connection.id == item_id: + await connection.send_text(message) + + async def broadcast(self, message: str): + for connection in self.active_connections: + await connection.send_text(message) + +manager = websocketConnectionManager() + +@core_app.websocket("/api/v1/ws/{item_id}") +async def websocket_endpoint(websocket: WebSocket, item_id: str): + await manager.connect(websocket, item_id) + try: + while True: + data = await websocket.receive_text() + except WebSocketDisconnect: + manager.disconnect(websocket) + +@core_app.post("/api/v1/ws/{item_id}") +async def websocket_endpoint(item_id: str, data: str): + await updater(item_id, data) + +async def updater(item_id, data): + return await manager.send_personal_message( + f"{data}", item_id + ) \ No newline at end of file From 51ca515d2631ebe9008b74876cd69552296c286b Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 23 Nov 2022 21:51:32 +0000 Subject: [PATCH 02/19] renamed for clarity --- lnbits/core/views/api.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 8f7c11a2e..41adf1a76 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -713,15 +713,11 @@ class websocketConnectionManager: def disconnect(self, websocket: WebSocket): self.active_connections.remove(websocket) - async def send_personal_message(self, message: str, item_id: str): + async def send_data(self, message: str, item_id: str): for connection in self.active_connections: if connection.id == item_id: await connection.send_text(message) - async def broadcast(self, message: str): - for connection in self.active_connections: - await connection.send_text(message) - manager = websocketConnectionManager() @core_app.websocket("/api/v1/ws/{item_id}") @@ -738,6 +734,6 @@ async def websocket_endpoint(item_id: str, data: str): await updater(item_id, data) async def updater(item_id, data): - return await manager.send_personal_message( + return await manager.send_data( f"{data}", item_id ) \ No newline at end of file From 4b707b5a30da4f81cbfae103859296eae7006746 Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 23 Nov 2022 22:22:33 +0000 Subject: [PATCH 03/19] updated function names --- lnbits/core/views/api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 41adf1a76..60a266511 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -721,7 +721,7 @@ class websocketConnectionManager: manager = websocketConnectionManager() @core_app.websocket("/api/v1/ws/{item_id}") -async def websocket_endpoint(websocket: WebSocket, item_id: str): +async def websocket_connect(websocket: WebSocket, item_id: str): await manager.connect(websocket, item_id) try: while True: @@ -730,7 +730,7 @@ async def websocket_endpoint(websocket: WebSocket, item_id: str): manager.disconnect(websocket) @core_app.post("/api/v1/ws/{item_id}") -async def websocket_endpoint(item_id: str, data: str): +async def websocket_update(item_id: str, data: str): await updater(item_id, data) async def updater(item_id, data): From 2f08255e9289a601e73ee10c24a603f7d24f43a5 Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 23 Nov 2022 22:27:09 +0000 Subject: [PATCH 04/19] added get option --- lnbits/core/views/api.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 60a266511..a35b55ada 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -733,6 +733,10 @@ async def websocket_connect(websocket: WebSocket, item_id: str): async def websocket_update(item_id: str, data: str): await updater(item_id, data) +@core_app.get("/api/v1/ws/{item_id}/{data}") +async def websocket_update(item_id: str, data: str): + await updater(item_id, data) + async def updater(item_id, data): return await manager.send_data( f"{data}", item_id From 152991fbec6b78b1d9949119419cc00d199f3068 Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 23 Nov 2022 22:31:11 +0000 Subject: [PATCH 05/19] added try for return --- lnbits/core/views/api.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index a35b55ada..b7bc7084b 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -731,11 +731,19 @@ async def websocket_connect(websocket: WebSocket, item_id: str): @core_app.post("/api/v1/ws/{item_id}") async def websocket_update(item_id: str, data: str): - await updater(item_id, data) + try: + await updater(item_id, data) + return {"sent": True, "data": data} + except: + return {"sent": False, "data": data} @core_app.get("/api/v1/ws/{item_id}/{data}") async def websocket_update(item_id: str, data: str): - await updater(item_id, data) + try: + await updater(item_id, data) + return {"sent": True, "data": data} + except: + return {"sent": False, "data": data} async def updater(item_id, data): return await manager.send_data( From cea4f9350c574a0b274ec967b3f7f493cd8c6374 Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 23 Nov 2022 22:42:32 +0000 Subject: [PATCH 06/19] black --- lnbits/core/views/api.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index b7bc7084b..2cc7aacbf 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -701,6 +701,7 @@ async def api_auditor(wallet: WalletTypeInfo = Depends(get_key_type)): ##################UNIVERSAL WEBSOCKET MANAGER######################## + class websocketConnectionManager: def __init__(self): self.active_connections: List[WebSocket] = [] @@ -718,8 +719,10 @@ class websocketConnectionManager: if connection.id == item_id: await connection.send_text(message) + manager = websocketConnectionManager() + @core_app.websocket("/api/v1/ws/{item_id}") async def websocket_connect(websocket: WebSocket, item_id: str): await manager.connect(websocket, item_id) @@ -729,6 +732,7 @@ async def websocket_connect(websocket: WebSocket, item_id: str): except WebSocketDisconnect: manager.disconnect(websocket) + @core_app.post("/api/v1/ws/{item_id}") async def websocket_update(item_id: str, data: str): try: @@ -737,6 +741,7 @@ async def websocket_update(item_id: str, data: str): except: return {"sent": False, "data": data} + @core_app.get("/api/v1/ws/{item_id}/{data}") async def websocket_update(item_id: str, data: str): try: @@ -745,7 +750,6 @@ async def websocket_update(item_id: str, data: str): except: return {"sent": False, "data": data} + async def updater(item_id, data): - return await manager.send_data( - f"{data}", item_id - ) \ No newline at end of file + return await manager.send_data(f"{data}", item_id) From fde128e96116797016b90cd01cbda12ff90cad1e Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 23 Nov 2022 23:35:02 +0000 Subject: [PATCH 07/19] Better naming --- lnbits/core/views/api.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 2cc7aacbf..e65206593 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -720,23 +720,23 @@ class websocketConnectionManager: await connection.send_text(message) -manager = websocketConnectionManager() +websocketManager = websocketConnectionManager() @core_app.websocket("/api/v1/ws/{item_id}") async def websocket_connect(websocket: WebSocket, item_id: str): - await manager.connect(websocket, item_id) + await websocketManager.connect(websocket, item_id) try: while True: data = await websocket.receive_text() except WebSocketDisconnect: - manager.disconnect(websocket) + websocketManager.disconnect(websocket) @core_app.post("/api/v1/ws/{item_id}") async def websocket_update(item_id: str, data: str): try: - await updater(item_id, data) + await websocketUpdater(item_id, data) return {"sent": True, "data": data} except: return {"sent": False, "data": data} @@ -745,11 +745,11 @@ async def websocket_update(item_id: str, data: str): @core_app.get("/api/v1/ws/{item_id}/{data}") async def websocket_update(item_id: str, data: str): try: - await updater(item_id, data) + await websocketUpdater(item_id, data) return {"sent": True, "data": data} except: return {"sent": False, "data": data} -async def updater(item_id, data): - return await manager.send_data(f"{data}", item_id) +async def websocketUpdater(item_id, data): + return await websocketManager.send_data(f"{data}", item_id) From f876f0659f6387f57597a17484c9b11126515490 Mon Sep 17 00:00:00 2001 From: ben Date: Thu, 24 Nov 2022 00:21:39 +0000 Subject: [PATCH 08/19] Moved into correct files, and added payment example --- lnbits/core/services.py | 24 +++++++++++++++++++++++- lnbits/core/tasks.py | 4 +++- lnbits/core/views/api.py | 27 ++------------------------- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/lnbits/core/services.py b/lnbits/core/services.py index 5d993b4c5..72a0c84b7 100644 --- a/lnbits/core/services.py +++ b/lnbits/core/services.py @@ -6,7 +6,7 @@ from typing import Dict, Optional, Tuple from urllib.parse import parse_qs, urlparse import httpx -from fastapi import Depends +from fastapi import Depends, WebSocket, WebSocketDisconnect from lnurl import LnurlErrorResponse from lnurl import decode as decode_lnurl # type: ignore from loguru import logger @@ -382,3 +382,25 @@ async def check_transaction_status( # WARN: this same value must be used for balance check and passed to WALLET.pay_invoice(), it may cause a vulnerability if the values differ def fee_reserve(amount_msat: int) -> int: return max(int(RESERVE_FEE_MIN), int(amount_msat * RESERVE_FEE_PERCENT / 100.0)) + +class websocketConnectionManager: + def __init__(self): + self.active_connections: List[WebSocket] = [] + + async def connect(self, websocket: WebSocket, item_id: str): + await websocket.accept() + websocket.id = item_id + self.active_connections.append(websocket) + + def disconnect(self, websocket: WebSocket): + self.active_connections.remove(websocket) + + async def send_data(self, message: str, item_id: str): + for connection in self.active_connections: + if connection.id == item_id: + await connection.send_text(message) + +websocketManager = websocketConnectionManager() + +async def websocketUpdater(item_id, data): + return await websocketManager.send_data(f"{data}", item_id) \ No newline at end of file diff --git a/lnbits/core/tasks.py b/lnbits/core/tasks.py index b57e26257..01d04a606 100644 --- a/lnbits/core/tasks.py +++ b/lnbits/core/tasks.py @@ -10,6 +10,7 @@ from lnbits.tasks import SseListenersDict, register_invoice_listener from . import db from .crud import get_balance_notify from .models import Payment +from .services import websocketUpdater api_invoice_listeners: Dict[str, asyncio.Queue] = SseListenersDict( "api_invoice_listeners" @@ -38,6 +39,7 @@ async def wait_for_paid_invoices(invoice_paid_queue: asyncio.Queue): logger.trace("received invoice paid event") # send information to sse channel await dispatch_api_invoice_listeners(payment) + await websocketUpdater(payment.wallet_id, payment) # dispatch webhook if payment.webhook and not payment.webhook_status: @@ -88,4 +90,4 @@ async def mark_webhook_sent(payment: Payment, status: int) -> None: WHERE hash = ? """, (status, payment.payment_hash), - ) + ) \ No newline at end of file diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index e65206593..3465ef248 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -56,6 +56,8 @@ from ..services import ( create_invoice, pay_invoice, perform_lnurlauth, + websocketManager, + websocketUpdater ) from ..tasks import api_invoice_listeners @@ -702,27 +704,6 @@ async def api_auditor(wallet: WalletTypeInfo = Depends(get_key_type)): ##################UNIVERSAL WEBSOCKET MANAGER######################## -class websocketConnectionManager: - def __init__(self): - self.active_connections: List[WebSocket] = [] - - async def connect(self, websocket: WebSocket, item_id: str): - await websocket.accept() - websocket.id = item_id - self.active_connections.append(websocket) - - def disconnect(self, websocket: WebSocket): - self.active_connections.remove(websocket) - - async def send_data(self, message: str, item_id: str): - for connection in self.active_connections: - if connection.id == item_id: - await connection.send_text(message) - - -websocketManager = websocketConnectionManager() - - @core_app.websocket("/api/v1/ws/{item_id}") async def websocket_connect(websocket: WebSocket, item_id: str): await websocketManager.connect(websocket, item_id) @@ -749,7 +730,3 @@ async def websocket_update(item_id: str, data: str): return {"sent": True, "data": data} except: return {"sent": False, "data": data} - - -async def websocketUpdater(item_id, data): - return await websocketManager.send_data(f"{data}", item_id) From c2a737ab84ae4260dcd3a7a2cb93e62ade72e9da Mon Sep 17 00:00:00 2001 From: ben Date: Thu, 24 Nov 2022 00:34:46 +0000 Subject: [PATCH 09/19] Black --- lnbits/core/services.py | 5 ++++- lnbits/core/tasks.py | 2 +- lnbits/core/views/api.py | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lnbits/core/services.py b/lnbits/core/services.py index 72a0c84b7..b03562d91 100644 --- a/lnbits/core/services.py +++ b/lnbits/core/services.py @@ -383,6 +383,7 @@ async def check_transaction_status( def fee_reserve(amount_msat: int) -> int: return max(int(RESERVE_FEE_MIN), int(amount_msat * RESERVE_FEE_PERCENT / 100.0)) + class websocketConnectionManager: def __init__(self): self.active_connections: List[WebSocket] = [] @@ -400,7 +401,9 @@ class websocketConnectionManager: if connection.id == item_id: await connection.send_text(message) + websocketManager = websocketConnectionManager() + async def websocketUpdater(item_id, data): - return await websocketManager.send_data(f"{data}", item_id) \ No newline at end of file + return await websocketManager.send_data(f"{data}", item_id) diff --git a/lnbits/core/tasks.py b/lnbits/core/tasks.py index 01d04a606..734c3f01f 100644 --- a/lnbits/core/tasks.py +++ b/lnbits/core/tasks.py @@ -90,4 +90,4 @@ async def mark_webhook_sent(payment: Payment, status: int) -> None: WHERE hash = ? """, (status, payment.payment_hash), - ) \ No newline at end of file + ) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 3465ef248..9a359c0af 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -56,8 +56,8 @@ from ..services import ( create_invoice, pay_invoice, perform_lnurlauth, - websocketManager, - websocketUpdater + websocketManager, + websocketUpdater, ) from ..tasks import api_invoice_listeners From dce4da96b71eeaa932bce183e436975b6cfb2e1f Mon Sep 17 00:00:00 2001 From: ben Date: Thu, 24 Nov 2022 00:46:39 +0000 Subject: [PATCH 10/19] fixed function name clash --- lnbits/core/views/api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 9a359c0af..65d7c03e7 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -715,7 +715,7 @@ async def websocket_connect(websocket: WebSocket, item_id: str): @core_app.post("/api/v1/ws/{item_id}") -async def websocket_update(item_id: str, data: str): +async def websocket_update_post(item_id: str, data: str): try: await websocketUpdater(item_id, data) return {"sent": True, "data": data} @@ -724,7 +724,7 @@ async def websocket_update(item_id: str, data: str): @core_app.get("/api/v1/ws/{item_id}/{data}") -async def websocket_update(item_id: str, data: str): +async def websocket_update_get(item_id: str, data: str): try: await websocketUpdater(item_id, data) return {"sent": True, "data": data} From 3cea3a0ba86f8c79d53690e4ad275bd00e5033de Mon Sep 17 00:00:00 2001 From: ben Date: Thu, 24 Nov 2022 11:18:38 +0000 Subject: [PATCH 11/19] Removed payments websocket example To be prob added back at a later date --- lnbits/core/tasks.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/lnbits/core/tasks.py b/lnbits/core/tasks.py index 734c3f01f..b57e26257 100644 --- a/lnbits/core/tasks.py +++ b/lnbits/core/tasks.py @@ -10,7 +10,6 @@ from lnbits.tasks import SseListenersDict, register_invoice_listener from . import db from .crud import get_balance_notify from .models import Payment -from .services import websocketUpdater api_invoice_listeners: Dict[str, asyncio.Queue] = SseListenersDict( "api_invoice_listeners" @@ -39,7 +38,6 @@ async def wait_for_paid_invoices(invoice_paid_queue: asyncio.Queue): logger.trace("received invoice paid event") # send information to sse channel await dispatch_api_invoice_listeners(payment) - await websocketUpdater(payment.wallet_id, payment) # dispatch webhook if payment.webhook and not payment.webhook_status: From 746e119046dad05207b3afd6f8c3c90944923b7c Mon Sep 17 00:00:00 2001 From: ben Date: Mon, 28 Nov 2022 13:04:35 +0000 Subject: [PATCH 12/19] added vlads suggestion --- lnbits/core/services.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lnbits/core/services.py b/lnbits/core/services.py index b03562d91..285525405 100644 --- a/lnbits/core/services.py +++ b/lnbits/core/services.py @@ -2,7 +2,7 @@ import asyncio import json from binascii import unhexlify from io import BytesIO -from typing import Dict, Optional, Tuple +from typing import Dict, Optional, Tuple, List from urllib.parse import parse_qs, urlparse import httpx @@ -384,25 +384,30 @@ def fee_reserve(amount_msat: int) -> int: return max(int(RESERVE_FEE_MIN), int(amount_msat * RESERVE_FEE_PERCENT / 100.0)) -class websocketConnectionManager: +class WebsocketConnectionManager: def __init__(self): self.active_connections: List[WebSocket] = [] + return async def connect(self, websocket: WebSocket, item_id: str): await websocket.accept() - websocket.id = item_id - self.active_connections.append(websocket) + if item_id not in self.active_connections: + websocket.id = item_id + self.active_connections.append(websocket) + return def disconnect(self, websocket: WebSocket): self.active_connections.remove(websocket) + return async def send_data(self, message: str, item_id: str): for connection in self.active_connections: if connection.id == item_id: await connection.send_text(message) + return -websocketManager = websocketConnectionManager() +websocketManager = WebsocketConnectionManager() async def websocketUpdater(item_id, data): From aefd1fad6926e94cdc57854d29731f40ea8d0787 Mon Sep 17 00:00:00 2001 From: ben Date: Mon, 28 Nov 2022 13:13:45 +0000 Subject: [PATCH 13/19] isort --- lnbits/core/services.py | 25 ++++++------------- lnbits/core/views/api.py | 47 ++++++++++-------------------------- lnbits/core/views/generic.py | 22 +++++------------ 3 files changed, 27 insertions(+), 67 deletions(-) diff --git a/lnbits/core/services.py b/lnbits/core/services.py index 285525405..798680a23 100644 --- a/lnbits/core/services.py +++ b/lnbits/core/services.py @@ -2,7 +2,7 @@ import asyncio import json from binascii import unhexlify from io import BytesIO -from typing import Dict, Optional, Tuple, List +from typing import Dict, List, Optional, Tuple from urllib.parse import parse_qs, urlparse import httpx @@ -13,27 +13,18 @@ from loguru import logger from lnbits import bolt11 from lnbits.db import Connection -from lnbits.decorators import ( - WalletTypeInfo, - get_key_type, - require_admin_key, - require_invoice_key, -) +from lnbits.decorators import (WalletTypeInfo, get_key_type, require_admin_key, + require_invoice_key) from lnbits.helpers import url_for, urlsafe_short_hash from lnbits.requestvars import g -from lnbits.settings import FAKE_WALLET, RESERVE_FEE_MIN, RESERVE_FEE_PERCENT, WALLET +from lnbits.settings import (FAKE_WALLET, RESERVE_FEE_MIN, RESERVE_FEE_PERCENT, + WALLET) from lnbits.wallets.base import PaymentResponse, PaymentStatus from . import db -from .crud import ( - check_internal, - create_payment, - delete_wallet_payment, - get_wallet, - get_wallet_payment, - update_payment_details, - update_payment_status, -) +from .crud import (check_internal, create_payment, delete_wallet_payment, + get_wallet, get_wallet_payment, update_payment_details, + update_payment_status) from .models import Payment try: diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index ac56eb5ed..c6c5f9925 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -9,10 +9,10 @@ from io import BytesIO from typing import Dict, List, Optional, Tuple, Union from urllib.parse import ParseResult, parse_qs, urlencode, urlparse, urlunparse -import async_timeout import httpx import pyqrcode -from fastapi import Depends, Header, Query, Request, WebSocket, WebSocketDisconnect +from fastapi import (Depends, Header, Query, Request, WebSocket, + WebSocketDisconnect) from fastapi.exceptions import HTTPException from fastapi.params import Body from loguru import logger @@ -21,44 +21,23 @@ from pydantic.fields import Field from sse_starlette.sse import EventSourceResponse, ServerSentEvent from starlette.responses import HTMLResponse, StreamingResponse +import async_timeout from lnbits import bolt11, lnurl from lnbits.core.models import Payment, Wallet -from lnbits.decorators import ( - WalletTypeInfo, - get_key_type, - require_admin_key, - require_invoice_key, -) +from lnbits.decorators import (WalletTypeInfo, get_key_type, require_admin_key, + require_invoice_key) from lnbits.helpers import url_for, urlsafe_short_hash from lnbits.settings import LNBITS_ADMIN_USERS, LNBITS_SITE_TITLE, WALLET -from lnbits.utils.exchange_rates import ( - currencies, - fiat_amount_as_satoshis, - satoshis_amount_as_fiat, -) +from lnbits.utils.exchange_rates import (currencies, fiat_amount_as_satoshis, + satoshis_amount_as_fiat) from .. import core_app, db -from ..crud import ( - create_payment, - get_payments, - get_standalone_payment, - get_total_balance, - get_wallet, - get_wallet_for_key, - save_balance_check, - update_payment_status, - update_wallet, -) -from ..services import ( - InvoiceFailure, - PaymentFailure, - check_transaction_status, - create_invoice, - pay_invoice, - perform_lnurlauth, - websocketManager, - websocketUpdater, -) +from ..crud import (create_payment, get_payments, get_standalone_payment, + get_total_balance, get_wallet, get_wallet_for_key, + save_balance_check, update_payment_status, update_wallet) +from ..services import (InvoiceFailure, PaymentFailure, + check_transaction_status, create_invoice, pay_invoice, + perform_lnurlauth, websocketManager, websocketUpdater) from ..tasks import api_invoice_listeners diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py index 31a7b0300..c16c8a410 100644 --- a/lnbits/core/views/generic.py +++ b/lnbits/core/views/generic.py @@ -15,24 +15,14 @@ from lnbits.core import db from lnbits.core.models import User from lnbits.decorators import check_user_exists from lnbits.helpers import template_renderer, url_for -from lnbits.settings import ( - LNBITS_ADMIN_USERS, - LNBITS_ALLOWED_USERS, - LNBITS_CUSTOM_LOGO, - LNBITS_SITE_TITLE, - SERVICE_FEE, -) +from lnbits.settings import (LNBITS_ADMIN_USERS, LNBITS_ALLOWED_USERS, + LNBITS_CUSTOM_LOGO, LNBITS_SITE_TITLE, + SERVICE_FEE) from ...helpers import get_valid_extensions -from ..crud import ( - create_account, - create_wallet, - delete_wallet, - get_balance_check, - get_user, - save_balance_notify, - update_user_extension, -) +from ..crud import (create_account, create_wallet, delete_wallet, + get_balance_check, get_user, save_balance_notify, + update_user_extension) from ..services import pay_invoice, redeem_lnurl_withdraw core_html_routes: APIRouter = APIRouter(tags=["Core NON-API Website Routes"]) From bb84f6b0e8ea74db4b615ab8ae4da0ef4d2849ab Mon Sep 17 00:00:00 2001 From: ben Date: Mon, 28 Nov 2022 13:24:10 +0000 Subject: [PATCH 14/19] Black --- lnbits/core/services.py | 23 ++++++++++++------ lnbits/core/views/api.py | 45 ++++++++++++++++++++++++++---------- lnbits/core/views/generic.py | 22 +++++++++++++----- 3 files changed, 65 insertions(+), 25 deletions(-) diff --git a/lnbits/core/services.py b/lnbits/core/services.py index 798680a23..d0f6ebd35 100644 --- a/lnbits/core/services.py +++ b/lnbits/core/services.py @@ -13,18 +13,27 @@ from loguru import logger from lnbits import bolt11 from lnbits.db import Connection -from lnbits.decorators import (WalletTypeInfo, get_key_type, require_admin_key, - require_invoice_key) +from lnbits.decorators import ( + WalletTypeInfo, + get_key_type, + require_admin_key, + require_invoice_key, +) from lnbits.helpers import url_for, urlsafe_short_hash from lnbits.requestvars import g -from lnbits.settings import (FAKE_WALLET, RESERVE_FEE_MIN, RESERVE_FEE_PERCENT, - WALLET) +from lnbits.settings import FAKE_WALLET, RESERVE_FEE_MIN, RESERVE_FEE_PERCENT, WALLET from lnbits.wallets.base import PaymentResponse, PaymentStatus from . import db -from .crud import (check_internal, create_payment, delete_wallet_payment, - get_wallet, get_wallet_payment, update_payment_details, - update_payment_status) +from .crud import ( + check_internal, + create_payment, + delete_wallet_payment, + get_wallet, + get_wallet_payment, + update_payment_details, + update_payment_status, +) from .models import Payment try: diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index c6c5f9925..1a7ee8f53 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -11,8 +11,7 @@ from urllib.parse import ParseResult, parse_qs, urlencode, urlparse, urlunparse import httpx import pyqrcode -from fastapi import (Depends, Header, Query, Request, WebSocket, - WebSocketDisconnect) +from fastapi import Depends, Header, Query, Request, WebSocket, WebSocketDisconnect from fastapi.exceptions import HTTPException from fastapi.params import Body from loguru import logger @@ -24,20 +23,42 @@ from starlette.responses import HTMLResponse, StreamingResponse import async_timeout from lnbits import bolt11, lnurl from lnbits.core.models import Payment, Wallet -from lnbits.decorators import (WalletTypeInfo, get_key_type, require_admin_key, - require_invoice_key) +from lnbits.decorators import ( + WalletTypeInfo, + get_key_type, + require_admin_key, + require_invoice_key, +) from lnbits.helpers import url_for, urlsafe_short_hash from lnbits.settings import LNBITS_ADMIN_USERS, LNBITS_SITE_TITLE, WALLET -from lnbits.utils.exchange_rates import (currencies, fiat_amount_as_satoshis, - satoshis_amount_as_fiat) +from lnbits.utils.exchange_rates import ( + currencies, + fiat_amount_as_satoshis, + satoshis_amount_as_fiat, +) from .. import core_app, db -from ..crud import (create_payment, get_payments, get_standalone_payment, - get_total_balance, get_wallet, get_wallet_for_key, - save_balance_check, update_payment_status, update_wallet) -from ..services import (InvoiceFailure, PaymentFailure, - check_transaction_status, create_invoice, pay_invoice, - perform_lnurlauth, websocketManager, websocketUpdater) +from ..crud import ( + create_payment, + get_payments, + get_standalone_payment, + get_total_balance, + get_wallet, + get_wallet_for_key, + save_balance_check, + update_payment_status, + update_wallet, +) +from ..services import ( + InvoiceFailure, + PaymentFailure, + check_transaction_status, + create_invoice, + pay_invoice, + perform_lnurlauth, + websocketManager, + websocketUpdater, +) from ..tasks import api_invoice_listeners diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py index c16c8a410..31a7b0300 100644 --- a/lnbits/core/views/generic.py +++ b/lnbits/core/views/generic.py @@ -15,14 +15,24 @@ from lnbits.core import db from lnbits.core.models import User from lnbits.decorators import check_user_exists from lnbits.helpers import template_renderer, url_for -from lnbits.settings import (LNBITS_ADMIN_USERS, LNBITS_ALLOWED_USERS, - LNBITS_CUSTOM_LOGO, LNBITS_SITE_TITLE, - SERVICE_FEE) +from lnbits.settings import ( + LNBITS_ADMIN_USERS, + LNBITS_ALLOWED_USERS, + LNBITS_CUSTOM_LOGO, + LNBITS_SITE_TITLE, + SERVICE_FEE, +) from ...helpers import get_valid_extensions -from ..crud import (create_account, create_wallet, delete_wallet, - get_balance_check, get_user, save_balance_notify, - update_user_extension) +from ..crud import ( + create_account, + create_wallet, + delete_wallet, + get_balance_check, + get_user, + save_balance_notify, + update_user_extension, +) from ..services import pay_invoice, redeem_lnurl_withdraw core_html_routes: APIRouter = APIRouter(tags=["Core NON-API Website Routes"]) From 187d709098c4dd6819d682f210b2e501b37ad840 Mon Sep 17 00:00:00 2001 From: ben Date: Mon, 28 Nov 2022 13:28:11 +0000 Subject: [PATCH 15/19] isort --- lnbits/core/views/api.py | 45 +++++++++++----------------------------- 1 file changed, 12 insertions(+), 33 deletions(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 1a7ee8f53..c6c5f9925 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -11,7 +11,8 @@ from urllib.parse import ParseResult, parse_qs, urlencode, urlparse, urlunparse import httpx import pyqrcode -from fastapi import Depends, Header, Query, Request, WebSocket, WebSocketDisconnect +from fastapi import (Depends, Header, Query, Request, WebSocket, + WebSocketDisconnect) from fastapi.exceptions import HTTPException from fastapi.params import Body from loguru import logger @@ -23,42 +24,20 @@ from starlette.responses import HTMLResponse, StreamingResponse import async_timeout from lnbits import bolt11, lnurl from lnbits.core.models import Payment, Wallet -from lnbits.decorators import ( - WalletTypeInfo, - get_key_type, - require_admin_key, - require_invoice_key, -) +from lnbits.decorators import (WalletTypeInfo, get_key_type, require_admin_key, + require_invoice_key) from lnbits.helpers import url_for, urlsafe_short_hash from lnbits.settings import LNBITS_ADMIN_USERS, LNBITS_SITE_TITLE, WALLET -from lnbits.utils.exchange_rates import ( - currencies, - fiat_amount_as_satoshis, - satoshis_amount_as_fiat, -) +from lnbits.utils.exchange_rates import (currencies, fiat_amount_as_satoshis, + satoshis_amount_as_fiat) from .. import core_app, db -from ..crud import ( - create_payment, - get_payments, - get_standalone_payment, - get_total_balance, - get_wallet, - get_wallet_for_key, - save_balance_check, - update_payment_status, - update_wallet, -) -from ..services import ( - InvoiceFailure, - PaymentFailure, - check_transaction_status, - create_invoice, - pay_invoice, - perform_lnurlauth, - websocketManager, - websocketUpdater, -) +from ..crud import (create_payment, get_payments, get_standalone_payment, + get_total_balance, get_wallet, get_wallet_for_key, + save_balance_check, update_payment_status, update_wallet) +from ..services import (InvoiceFailure, PaymentFailure, + check_transaction_status, create_invoice, pay_invoice, + perform_lnurlauth, websocketManager, websocketUpdater) from ..tasks import api_invoice_listeners From 31d9f2e2ee6eb04c1a99cd8f7f54c9ecd607da12 Mon Sep 17 00:00:00 2001 From: ben Date: Tue, 29 Nov 2022 11:09:54 +0000 Subject: [PATCH 16/19] Removed returns and reverted socket check so multiple clients can join --- lnbits/core/services.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/lnbits/core/services.py b/lnbits/core/services.py index d0f6ebd35..8a88411a1 100644 --- a/lnbits/core/services.py +++ b/lnbits/core/services.py @@ -387,24 +387,19 @@ def fee_reserve(amount_msat: int) -> int: class WebsocketConnectionManager: def __init__(self): self.active_connections: List[WebSocket] = [] - return async def connect(self, websocket: WebSocket, item_id: str): await websocket.accept() - if item_id not in self.active_connections: - websocket.id = item_id - self.active_connections.append(websocket) - return + websocket.id = item_id + self.active_connections.append(websocket) def disconnect(self, websocket: WebSocket): self.active_connections.remove(websocket) - return async def send_data(self, message: str, item_id: str): for connection in self.active_connections: if connection.id == item_id: await connection.send_text(message) - return websocketManager = WebsocketConnectionManager() From 9a9733c1ce9d3e79b375c4319072384ceb5c667c Mon Sep 17 00:00:00 2001 From: ben Date: Tue, 29 Nov 2022 11:23:34 +0000 Subject: [PATCH 17/19] Auto stash before merge of "universalwebsocket" and "origin/universalwebsocket" --- lnbits/core/views/api.py | 47 ++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 835967789..b7d83565a 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -11,10 +11,7 @@ from urllib.parse import ParseResult, parse_qs, urlencode, urlparse, urlunparse import httpx import pyqrcode - -from fastapi import (Depends, Header, Query, Request, WebSocket, - WebSocketDisconnect) - +from fastapi import Depends, Header, Query, Request, WebSocket, WebSocketDisconnect from fastapi.exceptions import HTTPException from fastapi.params import Body from loguru import logger @@ -26,20 +23,42 @@ from starlette.responses import HTMLResponse, StreamingResponse import async_timeout from lnbits import bolt11, lnurl from lnbits.core.models import Payment, Wallet -from lnbits.decorators import (WalletTypeInfo, get_key_type, require_admin_key, - require_invoice_key) +from lnbits.decorators import ( + WalletTypeInfo, + get_key_type, + require_admin_key, + require_invoice_key, +) from lnbits.helpers import url_for, urlsafe_short_hash from lnbits.settings import LNBITS_ADMIN_USERS, LNBITS_SITE_TITLE, WALLET -from lnbits.utils.exchange_rates import (currencies, fiat_amount_as_satoshis, - satoshis_amount_as_fiat) +from lnbits.utils.exchange_rates import ( + currencies, + fiat_amount_as_satoshis, + satoshis_amount_as_fiat, +) from .. import core_app, db -from ..crud import (create_payment, get_payments, get_standalone_payment, - get_total_balance, get_wallet, get_wallet_for_key, - save_balance_check, update_payment_status, update_wallet) -from ..services import (InvoiceFailure, PaymentFailure, - check_transaction_status, create_invoice, pay_invoice, - perform_lnurlauth, websocketManager, websocketUpdater) +from ..crud import ( + create_payment, + get_payments, + get_standalone_payment, + get_total_balance, + get_wallet, + get_wallet_for_key, + save_balance_check, + update_payment_status, + update_wallet, +) +from ..services import ( + InvoiceFailure, + PaymentFailure, + check_transaction_status, + create_invoice, + pay_invoice, + perform_lnurlauth, + websocketManager, + websocketUpdater, +) from ..tasks import api_invoice_listeners From 4aeb7683e5b178475cb329aa520cade314290223 Mon Sep 17 00:00:00 2001 From: benarc Date: Thu, 1 Dec 2022 13:15:46 +0000 Subject: [PATCH 18/19] Added Response --- lnbits/core/views/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index b7d83565a..a2a937360 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -11,7 +11,7 @@ from urllib.parse import ParseResult, parse_qs, urlencode, urlparse, urlunparse import httpx import pyqrcode -from fastapi import Depends, Header, Query, Request, WebSocket, WebSocketDisconnect +from fastapi import Depends, Header, Query, Response, Request, WebSocket, WebSocketDisconnect from fastapi.exceptions import HTTPException from fastapi.params import Body from loguru import logger From a220acb5837f6624c550d8f161c4d61ebd890e8c Mon Sep 17 00:00:00 2001 From: benarc Date: Thu, 1 Dec 2022 14:41:57 +0000 Subject: [PATCH 19/19] Removed id, using param instead --- lnbits/bolt11.py | 6 +++--- lnbits/core/services.py | 12 ++++++------ lnbits/core/views/api.py | 14 +++++++++++--- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/lnbits/bolt11.py b/lnbits/bolt11.py index 32b43feb6..08f1f1e59 100644 --- a/lnbits/bolt11.py +++ b/lnbits/bolt11.py @@ -166,7 +166,7 @@ def lnencode(addr, privkey): if addr.amount: amount = Decimal(str(addr.amount)) # We can only send down to millisatoshi. - if amount * 10**12 % 10: + if amount * 10 ** 12 % 10: raise ValueError( "Cannot encode {}: too many decimal places".format(addr.amount) ) @@ -271,7 +271,7 @@ class LnAddr(object): def shorten_amount(amount): """Given an amount in bitcoin, shorten it""" # Convert to pico initially - amount = int(amount * 10**12) + amount = int(amount * 10 ** 12) units = ["p", "n", "u", "m", ""] for unit in units: if amount % 1000 == 0: @@ -290,7 +290,7 @@ def _unshorten_amount(amount: str) -> int: # * `u` (micro): multiply by 0.000001 # * `n` (nano): multiply by 0.000000001 # * `p` (pico): multiply by 0.000000000001 - units = {"p": 10**12, "n": 10**9, "u": 10**6, "m": 10**3} + units = {"p": 10 ** 12, "n": 10 ** 9, "u": 10 ** 6, "m": 10 ** 3} unit = str(amount)[-1] # BOLT #11: diff --git a/lnbits/core/services.py b/lnbits/core/services.py index 8a88411a1..623f78139 100644 --- a/lnbits/core/services.py +++ b/lnbits/core/services.py @@ -329,12 +329,12 @@ async def perform_lnurlauth( sign_len = 6 + r_len + s_len signature = BytesIO() - signature.write(0x30.to_bytes(1, "big", signed=False)) + signature.write(0x30 .to_bytes(1, "big", signed=False)) signature.write((sign_len - 2).to_bytes(1, "big", signed=False)) - signature.write(0x02.to_bytes(1, "big", signed=False)) + signature.write(0x02 .to_bytes(1, "big", signed=False)) signature.write(r_len.to_bytes(1, "big", signed=False)) signature.write(r) - signature.write(0x02.to_bytes(1, "big", signed=False)) + signature.write(0x02 .to_bytes(1, "big", signed=False)) signature.write(s_len.to_bytes(1, "big", signed=False)) signature.write(s) @@ -388,9 +388,9 @@ class WebsocketConnectionManager: def __init__(self): self.active_connections: List[WebSocket] = [] - async def connect(self, websocket: WebSocket, item_id: str): + async def connect(self, websocket: WebSocket): await websocket.accept() - websocket.id = item_id + logger.debug(websocket) self.active_connections.append(websocket) def disconnect(self, websocket: WebSocket): @@ -398,7 +398,7 @@ class WebsocketConnectionManager: async def send_data(self, message: str, item_id: str): for connection in self.active_connections: - if connection.id == item_id: + if connection.path_params["item_id"] == item_id: await connection.send_text(message) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index a2a937360..f78219bf1 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -9,9 +9,18 @@ from io import BytesIO from typing import Dict, List, Optional, Tuple, Union from urllib.parse import ParseResult, parse_qs, urlencode, urlparse, urlunparse +import async_timeout import httpx import pyqrcode -from fastapi import Depends, Header, Query, Response, Request, WebSocket, WebSocketDisconnect +from fastapi import ( + Depends, + Header, + Query, + Request, + Response, + WebSocket, + WebSocketDisconnect, +) from fastapi.exceptions import HTTPException from fastapi.params import Body from loguru import logger @@ -20,7 +29,6 @@ from pydantic.fields import Field from sse_starlette.sse import EventSourceResponse, ServerSentEvent from starlette.responses import HTMLResponse, StreamingResponse -import async_timeout from lnbits import bolt11, lnurl from lnbits.core.models import Payment, Wallet from lnbits.decorators import ( @@ -706,7 +714,7 @@ async def api_auditor(wallet: WalletTypeInfo = Depends(get_key_type)): @core_app.websocket("/api/v1/ws/{item_id}") async def websocket_connect(websocket: WebSocket, item_id: str): - await websocketManager.connect(websocket, item_id) + await websocketManager.connect(websocket) try: while True: data = await websocket.receive_text()