Added frontend pages and started making lnurl

This commit is contained in:
Ben Arc 2021-04-13 17:10:25 +01:00
parent 9ec208c434
commit df5e4cb9db
9 changed files with 330 additions and 98 deletions

View file

@ -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)

View file

@ -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/<copilot_id>", 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())

View file

@ -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'))
);
"""

View file

@ -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

View file

@ -133,9 +133,17 @@
dense
v-model.trim="formDialogCopilot.data.title"
type="text"
label="*Title"
label="Title"
></q-input>
<q-expansion-item
group="api"
dense
expand-separator
label="Payment threshold 1"
>
<q-card>
<q-card-section>
<div class="row">
<div class="col">
<q-select
@ -143,7 +151,7 @@
dense
v-model.trim="formDialogCopilot.data.animation1"
:options="options"
label="Animation 1"
label="Animation"
/>
</div>
@ -153,12 +161,33 @@
dense
v-model.trim="formDialogCopilot.data.animation1threshold"
type="number"
label="Threshold (at least)"
label="From *sats"
>
</q-input>
</div>
<div class="col q-pl-xs">
<q-input
filled
dense
v-model.trim="formDialogCopilot.data.animation1webhook"
type="number"
label="Webhook"
>
</q-input>
</div>
</div>
</q-card-section>
</q-card>
</q-expansion-item>
<q-expansion-item
group="api"
dense
expand-separator
label="Payment threshold 2"
>
<q-card>
<q-card-section>
<div class="row">
<div class="col">
<q-select
@ -166,21 +195,43 @@
dense
v-model.trim="formDialogCopilot.data.animation2"
:options="options"
label="Animation 2"
label="Animation"
/>
</div>
<div class="col q-pl-sm">
<div class="col q-pl-xs">
<q-input
filled
dense
v-model.trim="formDialogCopilot.data.animation2threshold"
type="number"
label="Threshold (at least)"
label="From *sats"
>
</q-input>
</div>
<div class="col q-pl-xs">
<q-input
filled
dense
v-model.trim="formDialogCopilot.data.animation2webhook"
type="number"
label="Webhook"
>
</q-input>
</div>
</div>
</q-card-section>
</q-card>
</q-expansion-item>
<q-expansion-item
group="api"
dense
expand-separator
label="Payment threshold 3"
>
<q-card>
<q-card-section>
<div class="row">
<div class="col">
<q-select
@ -188,28 +239,34 @@
dense
v-model.trim="formDialogCopilot.data.animation3"
:options="options"
label="Animation 3"
label="Animation"
/>
</div>
<div class="col q-pl-sm">
<div class="col q-pl-xs">
<q-input
filled
dense
v-model.trim="formDialogCopilot.data.animation3threshold"
type="number"
label="Threshold (at least)"
label="From *sats"
>
</q-input>
</div>
<div class="col q-pl-xs">
<q-input
filled
dense
v-model.trim="formDialogCopilot.data.animation3webhook"
type="number"
label="Webhook"
>
</q-input>
</div>
</div>
<q-input
filled
dense
v-model.trim="formDialogCopilot.data.amount"
type="number"
label="*Amount (sats)"
></q-input>
</q-card-section>
</q-card>
</q-expansion-item>
<q-input
filled
@ -220,13 +277,29 @@
label="Lnurl title (message with QR code)"
>
</q-input>
<div class="row">
<div class="col">
<div class="q-gutter-sm">
<q-checkbox
v-model="formDialogCopilot.data.show_message"
left-label
label="Show lnurl-pay messages? (available in some wallets)"
label="Show lnurl-pay messages?"
/>
</div>
</div>
<div class="col q-pl-xs">
<div class="q-gutter-sm">
<q-checkbox
v-model="formDialogCopilot.data.show_ack"
left-label
label="Show 'powered by LNbits'"
/>
</div>
</div>
</div>
<div class="row q-mt-lg">
<q-btn
unelevated
@ -370,6 +443,7 @@
show: false,
data: {
show_message: false,
show_ack: true,
description: '',
time: null,
amount: null

View file

@ -0,0 +1,51 @@
{% extends "public.html" %} {% block page %}
<div class="q-pa-sm theCard">
<q-card class="my-card">
<div class="column">
<center>
<div class="col theHeading">{{ copilot.title }}</div>
</center>
<div class="col">say what</div>
<div class="col" style="margin: 2px 15px; max-height: 100px">
<center>
<q-btn flat dense outline>Open compose window</q-btn>
</center>
</div>
<q-separator></q-separator>
<div class="col">
<div class="row">
<div class="col"></div>
<div class="col"></div>
</div>
<q-separator></q-separator>
</div>
</div>
</q-card>
</div>
{% endblock %} {% block scripts %}
<script src="{{ url_for('static', filename='vendor/vue-qrcode@1.0.2/vue-qrcode.min.js') }}"></script>
<script>
Vue.component(VueQrcode.name, VueQrcode)
new Vue({
el: '#vue',
mixins: [windowMixin],
data() {
return {
newProgress: 0.4,
counter: 1,
newTimeLeft: '',
lnbtc: true,
onbtc: false,
charge_time_elapsed: '{{charge.time_elapsed}}',
charge_amount: '{{charge.amount}}',
charge_balance: '{{charge.balance}}',
charge_paid: '{{charge.paid}}'
}
},
methods: {},
created: function () {}
})
</script>
{% endblock %}

View file

@ -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)

View file

@ -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):