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