diff --git a/lnbits/extensions/lnurlp/tasks.py b/lnbits/extensions/lnurlp/tasks.py
index b8da5e43..2b574d42 100644
--- a/lnbits/extensions/lnurlp/tasks.py
+++ b/lnbits/extensions/lnurlp/tasks.py
@@ -4,7 +4,6 @@ import json
import httpx
from loguru import logger
-from lnbits.core import db as core_db
from lnbits.core.crud import update_payment_extra
from lnbits.core.models import Payment
from lnbits.helpers import get_current_extension_name
@@ -22,9 +21,8 @@ async def wait_for_paid_invoices():
await on_invoice_paid(payment)
-async def on_invoice_paid(payment: Payment) -> None:
- if payment.extra.get("tag") != "lnurlp":
- # not an lnurlp invoice
+async def on_invoice_paid(payment: Payment):
+ if not payment.extra or payment.extra.get("tag") != "lnurlp":
return
if payment.extra.get("wh_status"):
@@ -35,22 +33,23 @@ async def on_invoice_paid(payment: Payment) -> None:
if pay_link and pay_link.webhook_url:
async with httpx.AsyncClient() as client:
try:
- kwargs = {
- "json": {
+ r: httpx.Response = await client.post(
+ pay_link.webhook_url,
+ json={
"payment_hash": payment.payment_hash,
"payment_request": payment.bolt11,
"amount": payment.amount,
"comment": payment.extra.get("comment"),
"lnurlp": pay_link.id,
+ "body": json.loads(pay_link.webhook_body)
+ if pay_link.webhook_body
+ else "",
},
- "timeout": 40,
- }
- if pay_link.webhook_body:
- kwargs["json"]["body"] = json.loads(pay_link.webhook_body)
- if pay_link.webhook_headers:
- kwargs["headers"] = json.loads(pay_link.webhook_headers)
-
- r: httpx.Response = await client.post(pay_link.webhook_url, **kwargs)
+ headers=json.loads(pay_link.webhook_headers)
+ if pay_link.webhook_headers
+ else None,
+ timeout=40,
+ )
await mark_webhook_sent(
payment.payment_hash,
r.status_code,
diff --git a/lnbits/extensions/lnurlp/views.py b/lnbits/extensions/lnurlp/views.py
index 4e9f487c..9bc78056 100644
--- a/lnbits/extensions/lnurlp/views.py
+++ b/lnbits/extensions/lnurlp/views.py
@@ -1,7 +1,6 @@
from http import HTTPStatus
-from fastapi import Request
-from fastapi.params import Depends
+from fastapi import Depends, Request
from fastapi.templating import Jinja2Templates
from starlette.exceptions import HTTPException
from starlette.responses import HTMLResponse
diff --git a/lnbits/extensions/lnurlp/views_api.py b/lnbits/extensions/lnurlp/views_api.py
index d5966bf6..0fa739b0 100644
--- a/lnbits/extensions/lnurlp/views_api.py
+++ b/lnbits/extensions/lnurlp/views_api.py
@@ -1,9 +1,7 @@
import json
from http import HTTPStatus
-from fastapi import Request
-from fastapi.param_functions import Query
-from fastapi.params import Depends
+from fastapi import Depends, Query, Request
from lnurl.exceptions import InvalidUrl as LnurlInvalidUrl # type: ignore
from starlette.exceptions import HTTPException
@@ -36,7 +34,8 @@ async def api_links(
wallet_ids = [wallet.wallet.id]
if all_wallets:
- wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
+ user = await get_user(wallet.wallet.user)
+ wallet_ids = user.wallet_ids if user else []
try:
return [
@@ -137,6 +136,7 @@ async def api_link_create_or_update(
link = await update_pay_link(**data.dict(), link_id=link_id)
else:
link = await create_pay_link(data, wallet_id=wallet.wallet.id)
+ assert link
return {**link.dict(), "lnurl": link.lnurl(request)}
diff --git a/lnbits/extensions/lnurlpayout/README.md b/lnbits/extensions/lnurlpayout/README.md
deleted file mode 100644
index ddf209fe..00000000
--- a/lnbits/extensions/lnurlpayout/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# LNURLPayOut
-
-## Auto-dump a wallets funds to an LNURLpay
diff --git a/lnbits/extensions/lnurlpayout/__init__.py b/lnbits/extensions/lnurlpayout/__init__.py
deleted file mode 100644
index 9962290c..00000000
--- a/lnbits/extensions/lnurlpayout/__init__.py
+++ /dev/null
@@ -1,25 +0,0 @@
-import asyncio
-
-from fastapi import APIRouter
-
-from lnbits.db import Database
-from lnbits.helpers import template_renderer
-from lnbits.tasks import catch_everything_and_restart
-
-db = Database("ext_lnurlpayout")
-
-lnurlpayout_ext: APIRouter = APIRouter(prefix="/lnurlpayout", tags=["lnurlpayout"])
-
-
-def lnurlpayout_renderer():
- return template_renderer(["lnbits/extensions/lnurlpayout/templates"])
-
-
-from .tasks import wait_for_paid_invoices
-from .views import * # noqa
-from .views_api import * # noqa
-
-
-def lnurlpayout_start():
- loop = asyncio.get_event_loop()
- loop.create_task(catch_everything_and_restart(wait_for_paid_invoices))
diff --git a/lnbits/extensions/lnurlpayout/config.json.example b/lnbits/extensions/lnurlpayout/config.json.example
deleted file mode 100644
index b4160d7b..00000000
--- a/lnbits/extensions/lnurlpayout/config.json.example
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "name": "LNURLPayout",
- "short_description": "Autodump wallet funds to LNURLpay",
- "icon": "exit_to_app",
- "contributors": ["arcbtc","talvasconcelos"]
-}
diff --git a/lnbits/extensions/lnurlpayout/crud.py b/lnbits/extensions/lnurlpayout/crud.py
deleted file mode 100644
index 0f9f98ac..00000000
--- a/lnbits/extensions/lnurlpayout/crud.py
+++ /dev/null
@@ -1,62 +0,0 @@
-from typing import List, Optional, Union
-
-from lnbits.helpers import urlsafe_short_hash
-
-from . import db
-from .models import CreateLnurlPayoutData, lnurlpayout
-
-
-async def create_lnurlpayout(
- wallet_id: str, admin_key: str, data: CreateLnurlPayoutData
-) -> lnurlpayout:
- lnurlpayout_id = urlsafe_short_hash()
- await db.execute(
- """
- INSERT INTO lnurlpayout.lnurlpayouts (id, title, wallet, admin_key, lnurlpay, threshold)
- VALUES (?, ?, ?, ?, ?, ?)
- """,
- (
- lnurlpayout_id,
- data.title,
- wallet_id,
- admin_key,
- data.lnurlpay,
- data.threshold,
- ),
- )
-
- lnurlpayout = await get_lnurlpayout(lnurlpayout_id)
- assert lnurlpayout, "Newly created lnurlpayout couldn't be retrieved"
- return lnurlpayout
-
-
-async def get_lnurlpayout(lnurlpayout_id: str) -> Optional[lnurlpayout]:
- row = await db.fetchone(
- "SELECT * FROM lnurlpayout.lnurlpayouts WHERE id = ?", (lnurlpayout_id,)
- )
- return lnurlpayout(**row) if row else None
-
-
-async def get_lnurlpayout_from_wallet(wallet_id: str) -> Optional[lnurlpayout]:
- row = await db.fetchone(
- "SELECT * FROM lnurlpayout.lnurlpayouts WHERE wallet = ?", (wallet_id,)
- )
- return lnurlpayout(**row) if row else None
-
-
-async def get_lnurlpayouts(wallet_ids: Union[str, List[str]]) -> List[lnurlpayout]:
- if isinstance(wallet_ids, str):
- wallet_ids = [wallet_ids]
-
- q = ",".join(["?"] * len(wallet_ids))
- rows = await db.fetchall(
- f"SELECT * FROM lnurlpayout.lnurlpayouts WHERE wallet IN ({q})", (*wallet_ids,)
- )
-
- return [lnurlpayout(**row) if row else None for row in rows]
-
-
-async def delete_lnurlpayout(lnurlpayout_id: str) -> None:
- await db.execute(
- "DELETE FROM lnurlpayout.lnurlpayouts WHERE id = ?", (lnurlpayout_id,)
- )
diff --git a/lnbits/extensions/lnurlpayout/migrations.py b/lnbits/extensions/lnurlpayout/migrations.py
deleted file mode 100644
index 7a45e495..00000000
--- a/lnbits/extensions/lnurlpayout/migrations.py
+++ /dev/null
@@ -1,16 +0,0 @@
-async def m001_initial(db):
- """
- Initial lnurlpayouts table.
- """
- await db.execute(
- f"""
- CREATE TABLE lnurlpayout.lnurlpayouts (
- id TEXT PRIMARY KEY,
- title TEXT NOT NULL,
- wallet TEXT NOT NULL,
- admin_key TEXT NOT NULL,
- lnurlpay TEXT NOT NULL,
- threshold {db.big_int} NOT NULL
- );
- """
- )
diff --git a/lnbits/extensions/lnurlpayout/models.py b/lnbits/extensions/lnurlpayout/models.py
deleted file mode 100644
index fc8be575..00000000
--- a/lnbits/extensions/lnurlpayout/models.py
+++ /dev/null
@@ -1,18 +0,0 @@
-from sqlite3 import Row
-
-from pydantic import BaseModel
-
-
-class CreateLnurlPayoutData(BaseModel):
- title: str
- lnurlpay: str
- threshold: int
-
-
-class lnurlpayout(BaseModel):
- id: str
- title: str
- wallet: str
- admin_key: str
- lnurlpay: str
- threshold: int
diff --git a/lnbits/extensions/lnurlpayout/tasks.py b/lnbits/extensions/lnurlpayout/tasks.py
deleted file mode 100644
index 71f299be..00000000
--- a/lnbits/extensions/lnurlpayout/tasks.py
+++ /dev/null
@@ -1,91 +0,0 @@
-import asyncio
-from http import HTTPStatus
-
-import httpx
-from loguru import logger
-from starlette.exceptions import HTTPException
-
-from lnbits.core import db as core_db
-from lnbits.core.crud import get_wallet
-from lnbits.core.models import Payment
-from lnbits.core.services import pay_invoice
-from lnbits.core.views.api import api_payments_decode
-from lnbits.helpers import get_current_extension_name
-from lnbits.tasks import register_invoice_listener
-
-from .crud import get_lnurlpayout_from_wallet
-
-
-async def wait_for_paid_invoices():
- invoice_queue = asyncio.Queue()
- register_invoice_listener(invoice_queue, get_current_extension_name())
-
- while True:
- payment = await invoice_queue.get()
- await on_invoice_paid(payment)
-
-
-async def on_invoice_paid(payment: Payment) -> None:
- try:
- # Check its got a payout associated with it
- lnurlpayout_link = await get_lnurlpayout_from_wallet(payment.wallet_id)
- logger.debug("LNURLpayout", lnurlpayout_link)
- if lnurlpayout_link:
-
- # Check the wallet balance is more than the threshold
-
- wallet = await get_wallet(lnurlpayout_link.wallet)
- threshold = lnurlpayout_link.threshold + (lnurlpayout_link.threshold * 0.02)
-
- if wallet.balance < threshold:
- return
- # Get the invoice from the LNURL to pay
- async with httpx.AsyncClient() as client:
- try:
- url = await api_payments_decode({"data": lnurlpayout_link.lnurlpay})
- if str(url["domain"])[0:4] != "http":
- raise HTTPException(
- status_code=HTTPStatus.FORBIDDEN, detail="LNURL broken"
- )
-
- try:
- r = await client.get(str(url["domain"]), timeout=40)
- res = r.json()
- try:
- r = await client.get(
- res["callback"]
- + "?amount="
- + str(
- int((wallet.balance - wallet.balance * 0.02) * 1000)
- ),
- timeout=40,
- )
- res = r.json()
-
- if hasattr(res, "status") and res["status"] == "ERROR":
- raise HTTPException(
- status_code=HTTPStatus.FORBIDDEN,
- detail=res["reason"],
- )
- try:
- await pay_invoice(
- wallet_id=payment.wallet_id,
- payment_request=res["pr"],
- extra={"tag": "lnurlpayout"},
- )
- return
- except:
- pass
-
- except Exception as e:
- print("ERROR", str(e))
- return
- except (httpx.ConnectError, httpx.RequestError):
- return
- except Exception:
- raise HTTPException(
- status_code=HTTPStatus.FORBIDDEN,
- detail="Failed to save LNURLPayout",
- )
- except:
- return
diff --git a/lnbits/extensions/lnurlpayout/templates/lnurlpayout/_api_docs.html b/lnbits/extensions/lnurlpayout/templates/lnurlpayout/_api_docs.html
deleted file mode 100644
index afe24c42..00000000
--- a/lnbits/extensions/lnurlpayout/templates/lnurlpayout/_api_docs.html
+++ /dev/null
@@ -1,119 +0,0 @@
-GET
- /lnurlpayout/api/v1/lnurlpayouts
- Headers
- {"X-Api-Key": <invoice_key>}
- Body (application/json)
-
- Returns 200 OK (application/json)
-
- [<lnurlpayout_object>, ...]
- Curl example
- curl -X GET {{ request.base_url }}lnurlpayout/api/v1/lnurlpayouts -H
- "X-Api-Key: <invoice_key>"
-
- POST
- /lnurlpayout/api/v1/lnurlpayouts
- Headers
- {"X-Api-Key": <invoice_key>}
- Body (application/json)
- {"name": <string>, "currency": <string*ie USD*>}
-
- Returns 201 CREATED (application/json)
-
- {"currency": <string>, "id": <string>, "name":
- <string>, "wallet": <string>}
- Curl example
- curl -X POST {{ request.base_url }}lnurlpayout/api/v1/lnurlpayouts -d
- '{"name": <string>, "currency": <string>}' -H
- "Content-type: application/json" -H "X-Api-Key: <admin_key>"
-
- DELETE
- /lnurlpayout/api/v1/lnurlpayouts/<lnurlpayout_id>
- Headers
- {"X-Api-Key": <admin_key>}
- Returns 204 NO CONTENT
-
- Curl example
- curl -X DELETE {{ request.base_url
- }}lnurlpayout/api/v1/lnurlpayouts/<lnurlpayout_id> -H
- "X-Api-Key: <admin_key>"
-
- GET
- /lnurlpayout/api/v1/lnurlpayouts/<lnurlpayout_id>
- Headers
- {"X-Api-Key": <invoice_key>}
- Body (application/json)
-
- Returns 200 OK (application/json)
-
- [<lnurlpayout_object>, ...]
- Curl example
- curl -X GET {{ request.base_url
- }}lnurlpayout/api/v1/lnurlpayouts/<lnurlpayout_id> -H
- "X-Api-Key: <invoice_key>"
-
-