Skip to content

Commit b034078

Browse files
authored
feat: use lnurl lib for views_lnurl (#24)
* feat: use lnurl lib for views_lnurl
1 parent 1cd4b33 commit b034078

5 files changed

Lines changed: 68 additions & 81 deletions

File tree

config.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
"name": "Streamer Copilot",
33
"short_description": "Video tips/animations/webhooks",
44
"tile": "/copilot/static/bitcoin-streaming.png",
5-
"min_lnbits_version": "1.0.0",
5+
"version": "1.1.0",
6+
"min_lnbits_version": "1.3.0",
67
"contributors": [
78
{
89
"name": "Ben Arc",

models.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
from fastapi import Query, Request
2-
from lnurl import encode as lnurl_encode
1+
from fastapi import Query
32
from pydantic import BaseModel
43

54

@@ -32,7 +31,7 @@ class Copilot(BaseModel):
3231
user: str | None
3332
title: str
3433
lnurl_toggle: int
35-
wallet: str | None
34+
wallet: str
3635
animation1: str | None
3736
animation2: str | None
3837
animation3: str | None
@@ -50,7 +49,3 @@ class Copilot(BaseModel):
5049
timestamp: int
5150
fullscreen_cam: int
5251
iframe_url: str | None
53-
54-
def lnurl(self, req: Request) -> str:
55-
url = str(req.url_for("copilot.lnurl_response", cp_id=self.id))
56-
return lnurl_encode(url)

templates/copilot/compose.html

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,7 @@
2222
<q-card-section>
2323
<div class="row">
2424
<div class="col" style="max-width: 100px">
25-
<lnbits-qrcode
26-
:value="chatUrl"
27-
class="rounded-borders"
28-
></lnbits-qrcode>
25+
<lnbits-qrcode :value="chatUrl"></lnbits-qrcode>
2926
</div>
3027
<div class="col">
3128
<div class="text-h6 q-ml-md">Trollbox</div>
@@ -74,17 +71,15 @@
7471
"
7572
>
7673
<div class="col">
77-
<a class="text-secondary" :href="'lightning:' + copilot.lnurl">
78-
<lnbits-qrcode
79-
:value="'lightning:' + copilot.lnurl"
80-
class="rounded-borders"
81-
></lnbits-qrcode>
82-
<center
83-
class="absolute-bottom"
84-
style="color: black; font-size: 20px"
85-
v-text="copilot.lnurl_title"
86-
></center>
87-
</a>
74+
<lnbits-qrcode-lnurl
75+
:url="url"
76+
:show-buttons="false"
77+
></lnbits-qrcode-lnurl>
78+
<center
79+
class="absolute-bottom"
80+
style="color: black; font-size: 20px"
81+
v-text="copilot.lnurl_title"
82+
></center>
8883
</div>
8984
</div>
9085

@@ -165,7 +160,7 @@
165160
copilot: {},
166161
animQueue: [],
167162
queue: false,
168-
lnurl: '',
163+
url: '',
169164
troll_box: false,
170165
trollbox: [],
171166
chatUrl: '',
@@ -318,6 +313,8 @@
318313
)
319314
.then(response => {
320315
this.copilot = response.data
316+
this.url =
317+
window.location.origin + '/copilot/lnurl/' + this.copilot.id
321318
})
322319
.catch(err => {
323320
LNbits.utils.notifyApiError(err)

views_api.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from http import HTTPStatus
22

3-
from fastapi import APIRouter, Depends, Request
3+
from fastapi import APIRouter, Depends
44
from fastapi.exceptions import HTTPException
55
from lnbits.core.models import WalletTypeInfo
66
from lnbits.core.services import websocket_updater
@@ -28,18 +28,14 @@ async def api_copilots_retrieve(wallet: WalletTypeInfo = Depends(require_invoice
2828
@copilot_api_router.get(
2929
"/api/v1/copilot/{copilot_id}", dependencies=[Depends(require_invoice_key)]
3030
)
31-
async def api_copilot_retrieve(
32-
req: Request,
33-
copilot_id: str,
34-
):
31+
async def api_copilot_retrieve(copilot_id: str) -> Copilot:
3532
copilot = await get_copilot(copilot_id)
3633
if not copilot:
3734
raise HTTPException(
38-
status_code=HTTPStatus.NOT_FOUND, detail="Copilot not found"
35+
status_code=HTTPStatus.NOT_FOUND, detail="Copilot not found."
3936
)
40-
if not copilot.lnurl_toggle:
41-
return copilot
42-
return {**copilot.dict(), **{"lnurl": copilot.lnurl(req)}}
37+
38+
return copilot
4339

4440

4541
@copilot_api_router.post("/api/v1/copilot")

views_lnurl.py

Lines changed: 46 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,76 @@
11
import json
2-
from http import HTTPStatus
32

43
from fastapi import APIRouter, Query, Request
5-
from fastapi.exceptions import HTTPException
6-
from fastapi.responses import HTMLResponse
74
from lnbits.core.services import create_invoice
8-
from lnurl.types import LnurlPayMetadata
5+
from lnurl import (
6+
CallbackUrl,
7+
LightningInvoice,
8+
LnurlErrorResponse,
9+
LnurlPayActionResponse,
10+
LnurlPayMetadata,
11+
LnurlPayResponse,
12+
MilliSatoshi,
13+
)
14+
from pydantic import parse_obj_as
915

1016
from .crud import get_copilot
1117

1218
copilot_lnurl_router = APIRouter()
1319

1420

15-
@copilot_lnurl_router.get(
16-
"/lnurl/{cp_id}", response_class=HTMLResponse, name="copilot.lnurl_response"
17-
)
18-
async def lnurl_response(req: Request, cp_id: str):
21+
@copilot_lnurl_router.get("/lnurl/{cp_id}", name="copilot.lnurl_response")
22+
async def lnurl_response(
23+
req: Request, cp_id: str
24+
) -> LnurlPayResponse | LnurlErrorResponse:
1925
cp = await get_copilot(cp_id)
2026
if not cp:
21-
raise HTTPException(
22-
status_code=HTTPStatus.NOT_FOUND, detail="Copilot not found"
23-
)
27+
return LnurlErrorResponse(reason="Copilot not found.")
2428

25-
pay_response = {
26-
"tag": "payRequest",
27-
"callback": str(req.url_for("copilot.lnurl_callback", cp_id=cp_id)),
28-
"metadata": LnurlPayMetadata(json.dumps([["text/plain", str(cp.lnurl_title)]])),
29-
"maxSendable": 50000000,
30-
"minSendable": 10000,
31-
}
29+
callback_url = parse_obj_as(
30+
CallbackUrl, str(req.url_for("copilot.lnurl_callback", cp_id=cp_id))
31+
)
32+
33+
pay_response = LnurlPayResponse(
34+
callback=callback_url,
35+
metadata=LnurlPayMetadata(json.dumps([["text/plain", str(cp.lnurl_title)]])),
36+
minSendable=MilliSatoshi(10000),
37+
maxSendable=MilliSatoshi(50000000),
38+
)
3239

3340
if cp.show_message:
34-
pay_response["commentAllowed"] = 300
35-
return json.dumps(pay_response)
41+
pay_response.commentAllowed = 300
42+
43+
return pay_response
3644

3745

3846
@copilot_lnurl_router.get("/lnurl/cb/{cp_id}", name="copilot.lnurl_callback")
3947
async def lnurl_callback(
4048
cp_id: str, amount: str = Query(None), comment: str = Query(None)
41-
):
49+
) -> LnurlPayActionResponse | LnurlErrorResponse:
4250
cp = await get_copilot(cp_id)
4351
if not cp:
44-
raise HTTPException(
45-
status_code=HTTPStatus.NOT_FOUND, detail="Copilot not found"
46-
)
47-
amount_received = int(amount)
52+
return LnurlErrorResponse(reason="Copilot not found.")
4853

54+
amount_received = int(amount)
55+
amount_rounded = round(amount_received / 1000)
4956
if amount_received < 10000:
50-
raise HTTPException(
51-
status_code=HTTPStatus.FORBIDDEN,
52-
detail=(
53-
"Amount {round(amount_received / 1000)} "
54-
"is smaller than minimum 10 sats."
55-
),
57+
return LnurlErrorResponse(
58+
reason=f"Amount {amount_rounded} is smaller than minimum 10 sats."
5659
)
5760
elif amount_received / 1000 > 10000000:
58-
raise HTTPException(
59-
status_code=HTTPStatus.FORBIDDEN,
60-
detail=(
61-
"Amount {round(amount_received / 1000)} "
62-
"is greater than maximum 50000."
63-
),
61+
return LnurlErrorResponse(
62+
reason=f"Amount {amount_rounded} is greater than maximum 10000000 sats."
6463
)
65-
comment = ""
64+
6665
if comment:
67-
if len(comment or "") > 300:
68-
raise HTTPException(
69-
status_code=HTTPStatus.FORBIDDEN,
70-
detail=(
71-
"Got a comment with {len(comment)} characters, "
66+
if len(comment) > 300:
67+
return LnurlErrorResponse(
68+
reason=(
69+
f"Got a comment with {len(comment)} characters, "
7270
"but can only accept 300"
73-
),
71+
)
7472
)
75-
if len(comment) < 1:
76-
comment = "none"
77-
assert cp.wallet, "Copilot wallet not found"
73+
7874
payment = await create_invoice(
7975
wallet_id=cp.wallet,
8076
amount=int(amount_received / 1000),
@@ -84,4 +80,6 @@ async def lnurl_callback(
8480
).encode(),
8581
extra={"tag": "copilot", "copilotid": cp.id, "comment": comment},
8682
)
87-
return {"pr": payment.bolt11, "routes": []}
83+
84+
invoice = parse_obj_as(LightningInvoice, payment.bolt11)
85+
return LnurlPayActionResponse(pr=invoice)

0 commit comments

Comments
 (0)