diff --git a/config.json b/config.json index cb36c61..d7034eb 100644 --- a/config.json +++ b/config.json @@ -2,7 +2,8 @@ "name": "Streamer Copilot", "short_description": "Video tips/animations/webhooks", "tile": "/copilot/static/bitcoin-streaming.png", - "min_lnbits_version": "1.0.0", + "version": "1.1.0", + "min_lnbits_version": "1.3.0", "contributors": [ { "name": "Ben Arc", diff --git a/models.py b/models.py index e832cc3..6721d78 100644 --- a/models.py +++ b/models.py @@ -1,5 +1,4 @@ -from fastapi import Query, Request -from lnurl import encode as lnurl_encode +from fastapi import Query from pydantic import BaseModel @@ -32,7 +31,7 @@ class Copilot(BaseModel): user: str | None title: str lnurl_toggle: int - wallet: str | None + wallet: str animation1: str | None animation2: str | None animation3: str | None @@ -50,7 +49,3 @@ class Copilot(BaseModel): timestamp: int fullscreen_cam: int iframe_url: str | None - - def lnurl(self, req: Request) -> str: - url = str(req.url_for("copilot.lnurl_response", cp_id=self.id)) - return lnurl_encode(url) diff --git a/templates/copilot/compose.html b/templates/copilot/compose.html index ce47a60..4e41b29 100644 --- a/templates/copilot/compose.html +++ b/templates/copilot/compose.html @@ -22,10 +22,7 @@
- +
Trollbox
@@ -74,17 +71,15 @@ " >
- - -
-
+ +
@@ -165,7 +160,7 @@ copilot: {}, animQueue: [], queue: false, - lnurl: '', + url: '', troll_box: false, trollbox: [], chatUrl: '', @@ -318,6 +313,8 @@ ) .then(response => { this.copilot = response.data + this.url = + window.location.origin + '/copilot/lnurl/' + this.copilot.id }) .catch(err => { LNbits.utils.notifyApiError(err) diff --git a/views_api.py b/views_api.py index 07de398..a32a962 100644 --- a/views_api.py +++ b/views_api.py @@ -1,6 +1,6 @@ from http import HTTPStatus -from fastapi import APIRouter, Depends, Request +from fastapi import APIRouter, Depends from fastapi.exceptions import HTTPException from lnbits.core.models import WalletTypeInfo from lnbits.core.services import websocket_updater @@ -28,18 +28,14 @@ async def api_copilots_retrieve(wallet: WalletTypeInfo = Depends(require_invoice @copilot_api_router.get( "/api/v1/copilot/{copilot_id}", dependencies=[Depends(require_invoice_key)] ) -async def api_copilot_retrieve( - req: Request, - copilot_id: str, -): +async def api_copilot_retrieve(copilot_id: str) -> Copilot: copilot = await get_copilot(copilot_id) if not copilot: raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="Copilot not found" + status_code=HTTPStatus.NOT_FOUND, detail="Copilot not found." ) - if not copilot.lnurl_toggle: - return copilot - return {**copilot.dict(), **{"lnurl": copilot.lnurl(req)}} + + return copilot @copilot_api_router.post("/api/v1/copilot") diff --git a/views_lnurl.py b/views_lnurl.py index 365eb2d..9053f65 100644 --- a/views_lnurl.py +++ b/views_lnurl.py @@ -1,80 +1,76 @@ import json -from http import HTTPStatus from fastapi import APIRouter, Query, Request -from fastapi.exceptions import HTTPException -from fastapi.responses import HTMLResponse from lnbits.core.services import create_invoice -from lnurl.types import LnurlPayMetadata +from lnurl import ( + CallbackUrl, + LightningInvoice, + LnurlErrorResponse, + LnurlPayActionResponse, + LnurlPayMetadata, + LnurlPayResponse, + MilliSatoshi, +) +from pydantic import parse_obj_as from .crud import get_copilot copilot_lnurl_router = APIRouter() -@copilot_lnurl_router.get( - "/lnurl/{cp_id}", response_class=HTMLResponse, name="copilot.lnurl_response" -) -async def lnurl_response(req: Request, cp_id: str): +@copilot_lnurl_router.get("/lnurl/{cp_id}", name="copilot.lnurl_response") +async def lnurl_response( + req: Request, cp_id: str +) -> LnurlPayResponse | LnurlErrorResponse: cp = await get_copilot(cp_id) if not cp: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="Copilot not found" - ) + return LnurlErrorResponse(reason="Copilot not found.") - pay_response = { - "tag": "payRequest", - "callback": str(req.url_for("copilot.lnurl_callback", cp_id=cp_id)), - "metadata": LnurlPayMetadata(json.dumps([["text/plain", str(cp.lnurl_title)]])), - "maxSendable": 50000000, - "minSendable": 10000, - } + callback_url = parse_obj_as( + CallbackUrl, str(req.url_for("copilot.lnurl_callback", cp_id=cp_id)) + ) + + pay_response = LnurlPayResponse( + callback=callback_url, + metadata=LnurlPayMetadata(json.dumps([["text/plain", str(cp.lnurl_title)]])), + minSendable=MilliSatoshi(10000), + maxSendable=MilliSatoshi(50000000), + ) if cp.show_message: - pay_response["commentAllowed"] = 300 - return json.dumps(pay_response) + pay_response.commentAllowed = 300 + + return pay_response @copilot_lnurl_router.get("/lnurl/cb/{cp_id}", name="copilot.lnurl_callback") async def lnurl_callback( cp_id: str, amount: str = Query(None), comment: str = Query(None) -): +) -> LnurlPayActionResponse | LnurlErrorResponse: cp = await get_copilot(cp_id) if not cp: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="Copilot not found" - ) - amount_received = int(amount) + return LnurlErrorResponse(reason="Copilot not found.") + amount_received = int(amount) + amount_rounded = round(amount_received / 1000) if amount_received < 10000: - raise HTTPException( - status_code=HTTPStatus.FORBIDDEN, - detail=( - "Amount {round(amount_received / 1000)} " - "is smaller than minimum 10 sats." - ), + return LnurlErrorResponse( + reason=f"Amount {amount_rounded} is smaller than minimum 10 sats." ) elif amount_received / 1000 > 10000000: - raise HTTPException( - status_code=HTTPStatus.FORBIDDEN, - detail=( - "Amount {round(amount_received / 1000)} " - "is greater than maximum 50000." - ), + return LnurlErrorResponse( + reason=f"Amount {amount_rounded} is greater than maximum 10000000 sats." ) - comment = "" + if comment: - if len(comment or "") > 300: - raise HTTPException( - status_code=HTTPStatus.FORBIDDEN, - detail=( - "Got a comment with {len(comment)} characters, " + if len(comment) > 300: + return LnurlErrorResponse( + reason=( + f"Got a comment with {len(comment)} characters, " "but can only accept 300" - ), + ) ) - if len(comment) < 1: - comment = "none" - assert cp.wallet, "Copilot wallet not found" + payment = await create_invoice( wallet_id=cp.wallet, amount=int(amount_received / 1000), @@ -84,4 +80,6 @@ async def lnurl_callback( ).encode(), extra={"tag": "copilot", "copilotid": cp.id, "comment": comment}, ) - return {"pr": payment.bolt11, "routes": []} + + invoice = parse_obj_as(LightningInvoice, payment.bolt11) + return LnurlPayActionResponse(pr=invoice)