diff --git a/lnbits/extensions/lnurlpayout/README.md b/lnbits/extensions/lnurlpayout/README.md deleted file mode 100644 index ddf209fe4..000000000 --- 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 9962290cd..000000000 --- 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 b4160d7bd..000000000 --- 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 ccbca1204..000000000 --- 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) 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 7a45e4953..000000000 --- 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 fc8be5759..000000000 --- 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 7de26d15d..000000000 --- a/lnbits/extensions/lnurlpayout/tasks.py +++ /dev/null @@ -1,87 +0,0 @@ -import asyncio -from http import HTTPStatus - -import httpx -from lnurl import decode as lnurl_decode -from loguru import logger -from starlette.exceptions import HTTPException - -from lnbits.core.crud import get_wallet -from lnbits.core.models import Payment -from lnbits.core.services import pay_invoice -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) - assert 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 = lnurl_decode(lnurlpayout_link.lnurlpay) - - try: - r = await client.get(str(url), 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 afe24c423..000000000 --- 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>" - -
-
-
-
diff --git a/lnbits/extensions/lnurlpayout/templates/lnurlpayout/index.html b/lnbits/extensions/lnurlpayout/templates/lnurlpayout/index.html deleted file mode 100644 index 982309494..000000000 --- a/lnbits/extensions/lnurlpayout/templates/lnurlpayout/index.html +++ /dev/null @@ -1,271 +0,0 @@ -{% extends "base.html" %} {% from "macros.jinja" import window_vars with context -%} {% block page %} -
-
- - - New LNURLPayout - - - - - -
-
-
LNURLPayout
-
-
- Export to CSV -
-
- - {% raw %} - - - - {% endraw %} - -
-
-
- -
- - -
- {{SITE_TITLE}} LNURLPayout extension -
-
- - - - {% include "lnurlpayout/_api_docs.html" %} - - - -
-
- - - - - - - - -
- Create LNURLPayout - Cancel -
-
-
-
-
-{% endblock %} {% block scripts %} {{ window_vars(user) }} - -{% endblock %} diff --git a/lnbits/extensions/lnurlpayout/views.py b/lnbits/extensions/lnurlpayout/views.py deleted file mode 100644 index c3c00c5b9..000000000 --- a/lnbits/extensions/lnurlpayout/views.py +++ /dev/null @@ -1,19 +0,0 @@ -from http import HTTPStatus - -from fastapi import Depends, Request -from fastapi.templating import Jinja2Templates -from starlette.responses import HTMLResponse - -from lnbits.core.models import User -from lnbits.decorators import check_user_exists - -from . import lnurlpayout_ext, lnurlpayout_renderer - -templates = Jinja2Templates(directory="templates") - - -@lnurlpayout_ext.get("/", response_class=HTMLResponse) -async def index(request: Request, user: User = Depends(check_user_exists)): - return lnurlpayout_renderer().TemplateResponse( - "lnurlpayout/index.html", {"request": request, "user": user.dict()} - ) diff --git a/lnbits/extensions/lnurlpayout/views_api.py b/lnbits/extensions/lnurlpayout/views_api.py deleted file mode 100644 index 02feba157..000000000 --- a/lnbits/extensions/lnurlpayout/views_api.py +++ /dev/null @@ -1,102 +0,0 @@ -from http import HTTPStatus - -from fastapi import Depends, Query -from lnurl import decode as lnurl_decode -from starlette.exceptions import HTTPException - -from lnbits.core.crud import get_user -from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key - -from . import lnurlpayout_ext -from .crud import ( - create_lnurlpayout, - delete_lnurlpayout, - get_lnurlpayout, - get_lnurlpayout_from_wallet, - get_lnurlpayouts, -) -from .models import CreateLnurlPayoutData - -# from .tasks import on_invoice_paid - - -@lnurlpayout_ext.get("/api/v1/lnurlpayouts", status_code=HTTPStatus.OK) -async def api_lnurlpayouts( - all_wallets: bool = Query(None), wallet: WalletTypeInfo = Depends(get_key_type) -): - wallet_ids = [wallet.wallet.id] - if all_wallets: - user = await get_user(wallet.wallet.user) - wallet_ids = user.wallet_ids if user else [] - - return [lnurlpayout.dict() for lnurlpayout in await get_lnurlpayouts(wallet_ids)] - - -@lnurlpayout_ext.post("/api/v1/lnurlpayouts", status_code=HTTPStatus.CREATED) -async def api_lnurlpayout_create( - data: CreateLnurlPayoutData, wallet: WalletTypeInfo = Depends(get_key_type) -): - if await get_lnurlpayout_from_wallet(wallet.wallet.id): - raise HTTPException( - status_code=HTTPStatus.FORBIDDEN, - detail="Wallet already has lnurlpayout set", - ) - _ = lnurl_decode(data.lnurlpay) - lnurlpayout = await create_lnurlpayout( - wallet_id=wallet.wallet.id, admin_key=wallet.wallet.adminkey, data=data - ) - - if not lnurlpayout: - raise HTTPException( - status_code=HTTPStatus.FORBIDDEN, detail="Failed to save LNURLPayout" - ) - - return lnurlpayout.dict() - - -@lnurlpayout_ext.delete("/api/v1/lnurlpayouts/{lnurlpayout_id}") -async def api_lnurlpayout_delete( - lnurlpayout_id: str, wallet: WalletTypeInfo = Depends(require_admin_key) -): - lnurlpayout = await get_lnurlpayout(lnurlpayout_id) - - if not lnurlpayout: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="lnurlpayout does not exist." - ) - - if lnurlpayout.wallet != wallet.wallet.id: - raise HTTPException( - status_code=HTTPStatus.FORBIDDEN, detail="Not your lnurlpayout." - ) - - await delete_lnurlpayout(lnurlpayout_id) - return "", HTTPStatus.NO_CONTENT - - -# TODO: what is this?! - -# @lnurlpayout_ext.get("/api/v1/lnurlpayouts/{lnurlpayout_id}", status_code=HTTPStatus.OK) -# async def api_lnurlpayout_check( -# lnurlpayout_id: str, wallet: WalletTypeInfo = Depends(get_key_type) -# ): -# lnurlpayout = await get_lnurlpayout(lnurlpayout_id) -# ## THIS -# mock_payment = Payment( -# checking_id="mock", -# pending=False, -# amount=1, -# fee=1, -# time=0000, -# bolt11="mock", -# preimage="mock", -# payment_hash="mock", -# wallet_id=lnurlpayout.wallet, -# ) -# ## INSTEAD OF THIS -# # payments = await get_payments( -# # wallet_id=lnurlpayout.wallet, complete=True, pending=False, outgoing=True, incoming=True -# # ) - -# result = await on_invoice_paid(mock_payment) -# return