11import json
2- from http import HTTPStatus
32
43from fastapi import APIRouter , Query , Request
5- from fastapi .exceptions import HTTPException
6- from fastapi .responses import HTMLResponse
74from 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
1016from .crud import get_copilot
1117
1218copilot_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" )
3947async 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