From df5e4cb9dbf1347258982edcb95489dc45b7a2a5 Mon Sep 17 00:00:00 2001 From: Ben Arc Date: Tue, 13 Apr 2021 17:10:25 +0100 Subject: [PATCH] Added frontend pages and started making lnurl --- lnbits/extensions/copilot/crud.py | 28 ++- lnbits/extensions/copilot/lnurl.py | 86 +++++++ lnbits/extensions/copilot/migrations.py | 8 +- lnbits/extensions/copilot/models.py | 13 +- .../copilot/{display.html => compose.html} | 0 .../copilot/templates/copilot/index.html | 234 ++++++++++++------ .../copilot/templates/copilot/panel.html | 51 ++++ lnbits/extensions/copilot/views.py | 2 +- lnbits/extensions/copilot/views_api.py | 6 +- 9 files changed, 330 insertions(+), 98 deletions(-) create mode 100644 lnbits/extensions/copilot/lnurl.py rename lnbits/extensions/copilot/templates/copilot/{display.html => compose.html} (100%) create mode 100644 lnbits/extensions/copilot/templates/copilot/panel.html diff --git a/lnbits/extensions/copilot/crud.py b/lnbits/extensions/copilot/crud.py index f209a0a9..99d582f2 100644 --- a/lnbits/extensions/copilot/crud.py +++ b/lnbits/extensions/copilot/crud.py @@ -21,9 +21,13 @@ async def create_copilot( animation1threshold: Optional[int] = None, animation2threshold: Optional[int] = None, animation3threshold: Optional[int] = None, - show_message: Optional[str] = None, - amount: Optional[int] = None, + animation1webhook: Optional[str] = None, + animation2webhook: Optional[str] = None, + animation3webhook: Optional[str] = None, lnurl_title: Optional[str] = None, + show_message: Optional[int] = None, + show_ack: Optional[int] = None, + amount_made: Optional[int] = None, ) -> Copilots: copilot_id = urlsafe_short_hash() @@ -39,11 +43,16 @@ async def create_copilot( animation1threshold, animation2threshold, animation3threshold, + animation1webhook, + animation2webhook, + animation3webhook, + lnurl_title, show_message, - amount, - lnurl_title + show_ack, + lnurl_title, + amount_made ) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, ( copilot_id, @@ -55,9 +64,14 @@ async def create_copilot( animation1threshold, animation2threshold, animation3threshold, + animation1webhook, + animation2webhook, + animation3webhook, + lnurl_title, show_message, - amount, - lnurl_title + show_ack, + lnurl_title, + 0 ), ) return await get_copilot(copilot_id) diff --git a/lnbits/extensions/copilot/lnurl.py b/lnbits/extensions/copilot/lnurl.py new file mode 100644 index 00000000..1ab8eec4 --- /dev/null +++ b/lnbits/extensions/copilot/lnurl.py @@ -0,0 +1,86 @@ +import hashlib +import math +from quart import jsonify, url_for, request +from lnurl import LnurlPayResponse, LnurlPayActionResponse, LnurlErrorResponse # type: ignore + +from lnbits.core.services import create_invoice + +from . import copilot_ext +from .crud import get_copilot + + +@copilot_ext.route("/lnurl/", methods=["GET"]) +async def lnurl_response(copilot_id): + copilot = await get_copilot(copilot_id) + if not copilot: + return jsonify({"status": "ERROR", "reason": "Copilot not found."}) + + resp = LnurlPayResponse( + callback=url_for( + "copilot.lnurl_callback", _external=True + ), + min_sendable=copilot.amount, + max_sendable=copilot.amount, + metadata=copilot.lnurl_title, + ) + + params = resp.dict() + params["commentAllowed"] = 300 + + return jsonify(params) + + +@copilot_ext.route("/lnurl/cb", methods=["GET"]) +async def lnurl_callback(): + + amount_received = int(request.args.get("amount")) + + if amount_received < track.amount: + return ( + jsonify( + LnurlErrorResponse( + reason=f"Amount {round(amount_received / 1000)} is smaller than minimum {math.floor(track.min_sendable)}." + ).dict() + ), + ) + elif track.max_sendable < amount_received: + return ( + jsonify( + LnurlErrorResponse( + reason=f"Amount {round(amount_received / 1000)} is greater than maximum {math.floor(track.max_sendable)}." + ).dict() + ), + ) + + comment = request.args.get("comment") + if len(comment or "") > 300: + return jsonify( + LnurlErrorResponse( + reason=f"Got a comment with {len(comment)} characters, but can only accept 300" + ).dict() + ) + + copilot = await get_copilot_by_track(track_id) + + payment_hash, payment_request = await create_invoice( + wallet_id=copilot.wallet, + amount=int(amount_received / 1000), + memo=await track.fullname(), + description_hash=hashlib.sha256( + (await track.lnurlpay_metadata()).encode("utf-8") + ).digest(), + extra={"tag": "copilot", "track": track.id, "comment": comment}, + ) + + if amount_received < track.price_msat: + success_action = None + ecopilote: + success_action = track.success_action(payment_hash) + + resp = LnurlPayActionResponse( + pr=payment_request, + success_action=success_action, + routes=[], + ) + + return jsonify(resp.dict()) diff --git a/lnbits/extensions/copilot/migrations.py b/lnbits/extensions/copilot/migrations.py index d0ef2d5c..bb282837 100644 --- a/lnbits/extensions/copilot/migrations.py +++ b/lnbits/extensions/copilot/migrations.py @@ -15,9 +15,13 @@ async def m001_initial(db): animation1threshold INTEGER, animation2threshold INTEGER, animation3threshold INTEGER, - show_message TEXT, - amount INTEGER, + animation1webhook TEXT, + animation2webhook TEXT, + animation3webhook TEXT, lnurl_title TEXT, + show_message INTEGER, + show_ack INTEGER, + amount_made INTEGER, timestamp TIMESTAMP NOT NULL DEFAULT (strftime('%s', 'now')) ); """ diff --git a/lnbits/extensions/copilot/models.py b/lnbits/extensions/copilot/models.py index f083f0d3..e2b58aba 100644 --- a/lnbits/extensions/copilot/models.py +++ b/lnbits/extensions/copilot/models.py @@ -13,17 +13,16 @@ class Copilots(NamedTuple): animation1threshold: int animation2threshold: int animation3threshold: int + animation1webhook: str + animation2webhook: str + animation3webhook: str show_message: bool amount: int lnurl_title: str + show_message: int + show_ack: int + amount_made: int @classmethod def from_row(cls, row: Row) -> "Copilots": return cls(**dict(row)) - - @property - def paid(self): - if self.balance >= self.amount: - return True - else: - return False diff --git a/lnbits/extensions/copilot/templates/copilot/display.html b/lnbits/extensions/copilot/templates/copilot/compose.html similarity index 100% rename from lnbits/extensions/copilot/templates/copilot/display.html rename to lnbits/extensions/copilot/templates/copilot/compose.html diff --git a/lnbits/extensions/copilot/templates/copilot/index.html b/lnbits/extensions/copilot/templates/copilot/index.html index 70ce9192..581ce2ec 100644 --- a/lnbits/extensions/copilot/templates/copilot/index.html +++ b/lnbits/extensions/copilot/templates/copilot/index.html @@ -133,83 +133,140 @@ dense v-model.trim="formDialogCopilot.data.title" type="text" - label="*Title" + label="Title" > -
-
- -
- -
- - -
-
- -
-
- -
-
- - -
-
- -
-
- -
-
- - -
-
- - + expand-separator + label="Payment threshold 1" + > + + +
+
+ +
+ +
+ + +
+
+ + +
+
+
+
+ + + + + +
+
+ +
+ +
+ + +
+
+ + +
+
+
+
+
+ + + + +
+
+ +
+ +
+ + +
+
+ + +
+
+
+
+
-
- + +
+
+
+ +
+
+ +
+
+ +
+
+
+ +
+
+
{{ copilot.title }}
+
+
say what
+
+
+ Open compose window +
+
+ +
+
+
+
+
+ +
+
+
+
+ +{% endblock %} {% block scripts %} + + +{% endblock %} diff --git a/lnbits/extensions/copilot/views.py b/lnbits/extensions/copilot/views.py index a63782f7..7d6408b0 100644 --- a/lnbits/extensions/copilot/views.py +++ b/lnbits/extensions/copilot/views.py @@ -19,4 +19,4 @@ async def display(copilot_id): copilot = await get_copilot(copilot_id) or abort( HTTPStatus.NOT_FOUND, "Charge link does not exist." ) - return await render_template("copilot/display.html", copilot=copilot) + return await render_template("copilot/panel.html", copilot=copilot) diff --git a/lnbits/extensions/copilot/views_api.py b/lnbits/extensions/copilot/views_api.py index 69cdff7f..6d595ca2 100644 --- a/lnbits/extensions/copilot/views_api.py +++ b/lnbits/extensions/copilot/views_api.py @@ -30,8 +30,12 @@ from .crud import ( "animation1threshold": {"type": "integer"}, "animation2threshold": {"type": "integer"}, "animation3threshold": {"type": "integer"}, + "animation1webhook": {"type": "string"}, + "animation2webhook": {"type": "string"}, + "animation3webhook": {"type": "string"}, + "lnurl_title": {"type": "string", "empty": False, "required": True}, "show_message": {"type": "integer", "empty": False, "required": True}, - "amount": {"type": "integer", "empty": False, "required": True}, + "show_ack": {"type": "integer", "empty": False, "required": True}, } ) async def api_copilot_create_or_update(copilot_id=None):