Very hacky checker for websockets

Looking pretty fucking dope 


Added some extra gifs


Added wallet, lnurl generation should work


Damn you lnurl


check https for wss


added z for lnurlp


zindex to the moon


zindex to moon


lnurl too big in ambition


lnurl big


copy/paste bug address


attach lnurl to view


please work


lnurl bug


lnurl bug


bigger lnurl


added reconect button


me bug


await a string
This commit is contained in:
Ben Arc 2021-04-14 23:45:28 +01:00
parent 2a3e1c7f8a
commit a6a917fb5e
14 changed files with 401 additions and 98 deletions

View File

@ -9,3 +9,4 @@ copilot_ext: Blueprint = Blueprint(
from .views_api import * # noqa
from .views import * # noqa
from .lnurl import * # noqa

View File

@ -15,6 +15,7 @@ from quart import jsonify
async def create_copilot(
title: str,
user: str,
wallet: str,
animation1: Optional[str] = None,
animation2: Optional[str] = None,
animation3: Optional[str] = None,
@ -36,6 +37,7 @@ async def create_copilot(
INSERT INTO copilots (
id,
user,
wallet,
title,
animation1,
animation2,
@ -52,11 +54,12 @@ async def create_copilot(
lnurl_title,
amount_made
)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
copilot_id,
user,
wallet,
title,
animation1,
animation2,

View File

@ -9,19 +9,19 @@ 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:
@copilot_ext.route("/lnurl/<cp_id>", methods=["GET"])
async def lnurl_response(cp_id):
cp = await get_copilot(cp_id)
if not cp:
return jsonify({"status": "ERROR", "reason": "Copilot not found."})
resp = LnurlPayResponse(
callback=url_for(
"copilot.lnurl_callback", _external=True
"copilot.lnurl_callback", cp_id=cp_id, _external=True
),
min_sendable=copilot.amount,
max_sendable=copilot.amount,
metadata=copilot.lnurl_title,
min_sendable=10,
max_sendable=50000,
metadata=cp.lnurl_title,
)
params = resp.dict()
@ -30,24 +30,27 @@ async def lnurl_response(copilot_id):
return jsonify(params)
@copilot_ext.route("/lnurl/cb", methods=["GET"])
async def lnurl_callback():
@copilot_ext.route("/lnurl/cb/<cp_id>", methods=["GET"])
async def lnurl_callback(cp_id):
cp = await get_copilot(cp_id)
if not cp:
return jsonify({"status": "ERROR", "reason": "Copilot not found."})
amount_received = int(request.args.get("amount"))
if amount_received < track.amount:
if amount_received < 10:
return (
jsonify(
LnurlErrorResponse(
reason=f"Amount {round(amount_received / 1000)} is smaller than minimum {math.floor(track.min_sendable)}."
reason=f"Amount {round(amount_received / 1000)} is smaller than minimum 10 sats."
).dict()
),
)
elif track.max_sendable < amount_received:
elif 50000 > amount_received/1000:
return (
jsonify(
LnurlErrorResponse(
reason=f"Amount {round(amount_received / 1000)} is greater than maximum {math.floor(track.max_sendable)}."
reason=f"Amount {round(amount_received / 1000)} is greater than maximum 50000."
).dict()
),
)
@ -60,21 +63,19 @@ async def lnurl_callback():
).dict()
)
copilot = await get_copilot_by_track(track_id)
payment_hash, payment_request = await create_invoice(
wallet_id=copilot.wallet,
wallet_id=cp.wallet,
amount=int(amount_received / 1000),
memo=await track.fullname(),
memo=cp.lnurl_title,
description_hash=hashlib.sha256(
(await track.lnurlpay_metadata()).encode("utf-8")
(cp.lnurl_title).encode("utf-8")
).digest(),
extra={"tag": "copilot", "track": track.id, "comment": comment},
extra={"tag": "copilot", "comment": comment},
)
if amount_received < track.price_msat:
success_action = None
ecopilote:
else:
success_action = track.success_action(payment_hash)
resp = LnurlPayActionResponse(
@ -82,5 +83,8 @@ async def lnurl_callback():
success_action=success_action,
routes=[],
)
return jsonify(resp.dict())
socket_sendererer = app.socket_sendererer()
async with socket_sendererer.websocket('/ws') as the_websocket:
await the_websocket.send("pay{payment_hash}")
return jsonify(resp.dict())

View File

@ -9,6 +9,7 @@ async def m001_initial(db):
id TEXT NOT NULL PRIMARY KEY,
user TEXT,
title TEXT,
wallet TEXT,
animation1 TEXT,
animation2 TEXT,
animation3 TEXT,

View File

@ -1,12 +1,16 @@
from sqlite3 import Row
from typing import NamedTuple
import time
from quart import url_for
from lnurl import Lnurl, encode as lnurl_encode # type: ignore
from lnurl.types import LnurlPayMetadata # type: ignore
from lnurl.models import LnurlPaySuccessAction, UrlAction # type: ignore
class Copilots(NamedTuple):
id: str
user: str
title: str
wallet: str
animation1: str
animation2: str
animation3: str
@ -28,3 +32,8 @@ class Copilots(NamedTuple):
@classmethod
def from_row(cls, row: Row) -> "Copilots":
return cls(**dict(row))
@property
def lnurl(self) -> Lnurl:
url = url_for("copilot.lnurl_response", cp_id=self.id, _external=True)
return lnurl_encode(url)

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 504 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

View File

@ -1,17 +1,32 @@
{% extends "public.html" %} {% block page %}<q-page
>fdgasdf
{% extends "public.html" %} {% block page %}<q-page>
<iframe
width="100%"
height="100%"
id="iframe_main"
src="/"
class="fixed-bottom-left"
>
</iframe>
<video
autoplay="true"
id="videoElement"
style="width: 100%"
class="fixed-top-left"
class="fixed-bottom-right"
></video>
<img src="" />
<img
src="/copilot/static/confetti.gif"
style="width: 100%"
class="fixed-top-left"
<img src="" style="width: 100%" id="animations" class="fixed-bottom-left" />
<qrcode
style="width: 20%; z-index: 9999"
:value="'{{ lnurl }}'"
:options="{width:222}"
class="rounded-borders fixed-top-right"
></qrcode>
<q-btn
color="primary"
@click="reconnect"
label="Reconnect"
class="fixed-bottom-left"
/>
</q-page>
{% endblock %} {% block scripts %}
@ -39,6 +54,10 @@
return {}
},
methods: {
openURL: function (url) {
console.log(url)
return Quasar.utils.openURL(url)
},
initCamera() {
var video = document.querySelector('#videoElement')
@ -52,6 +71,88 @@
console.log('Something went wrong!')
})
}
},
animation1: function () {
self = this
setTimeout(function () {
setInterval(function () {
self.connection.send('')
}, 1000)
}, 2000)
},
reconnect: function () {
this.connection.addEventListener('open', function (event) {
this.connection.send('')
})
this.connection.addEventListener('message', function (event) {
res = event.data.split('-')
console.log(res[1])
if (res[0] != this.oldRes) {
this.oldRes = res[0]
if (res[1] == 'rocket') {
document.getElementById('animations').style.width = '50%'
document.getElementById('animations').src =
'/copilot/static/rocket.gif'
setTimeout(function () {
document.getElementById('animations').src = ''
}, 5000)
}
if (res[1] == 'face') {
document.getElementById('animations').style.width = '50%'
document.getElementById('animations').src =
'/copilot/static/face.gif'
setTimeout(function () {
document.getElementById('animations').src = ''
}, 5000)
}
if (res[1] == 'bitcoin') {
document.getElementById('animations').style.width = '30%'
document.getElementById('animations').src =
'/copilot/static/bitcoin.gif'
setTimeout(function () {
document.getElementById('animations').src = ''
}, 5000)
}
if (res[1] == 'confetti') {
document.getElementById('animations').style.width = '100%'
document.getElementById('animations').src =
'/copilot/static/confetti.gif'
setTimeout(function () {
document.getElementById('animations').src = ''
}, 5000)
}
if (res[1] == 'martijn') {
document.getElementById('animations').style.width = '50%'
document.getElementById('animations').src =
'/copilot/static/martijn.gif'
setTimeout(function () {
document.getElementById('animations').src = ''
}, 5000)
}
if (res[1] == 'rick') {
document.getElementById('animations').style.width = '50%'
document.getElementById('animations').src =
'/copilot/static/rick.gif'
setTimeout(function () {
document.getElementById('animations').src = ''
}, 5000)
}
if (res[1] == 'true') {
document.getElementById('videoElement').style.width = '20%'
}
if (res[1] == 'false') {
document.getElementById('videoElement').style.width = '100%'
}
if (res[1].substring(0, 3) == 'htt') {
document.getElementById('iframe_main').src = res[1]
}
}
})
this.connection.addEventListener('close', function (event) {
console.log('The connection has been closed')
})
}
},
mounted() {
@ -59,20 +160,98 @@
},
created: function () {
console.log('{{ copilot.id }}')
console.log('Starting connection to WebSocket Server')
this.connection = new WebSocket(
'wss://' + document.domain + ':' + location.port + '/ws'
)
this.connection.onmessage = function (event) {
console.log(event)
if (location.protocol !== 'http:') {
this.connection = new WebSocket(
'wss://' +
document.domain +
':' +
location.port +
'/copilot/ws/compose/{{ copilot.id }}'
)
} else {
this.connection = new WebSocket(
'ws://' +
document.domain +
':' +
location.port +
'/copilot/ws/compose/{{ copilot.id }}'
)
}
this.connection.onopen = function (event) {
console.log(event)
console.log('Successfully connected to the echo websocket server...')
}
this.connection.addEventListener('open', function (event) {
this.connection.send('')
})
this.connection.addEventListener('message', function (event) {
res = event.data.split('-')
console.log(res[1])
if (res[0] != this.oldRes) {
this.oldRes = res[0]
if (res[1] == 'rocket') {
document.getElementById('animations').style.width = '50%'
document.getElementById('animations').src =
'/copilot/static/rocket.gif'
setTimeout(function () {
document.getElementById('animations').src = ''
}, 5000)
}
if (res[1] == 'face') {
document.getElementById('animations').style.width = '50%'
document.getElementById('animations').src =
'/copilot/static/face.gif'
setTimeout(function () {
document.getElementById('animations').src = ''
}, 5000)
}
if (res[1] == 'bitcoin') {
document.getElementById('animations').style.width = '30%'
document.getElementById('animations').src =
'/copilot/static/bitcoin.gif'
setTimeout(function () {
document.getElementById('animations').src = ''
}, 5000)
}
if (res[1] == 'confetti') {
document.getElementById('animations').style.width = '100%'
document.getElementById('animations').src =
'/copilot/static/confetti.gif'
setTimeout(function () {
document.getElementById('animations').src = ''
}, 5000)
}
if (res[1] == 'martijn') {
document.getElementById('animations').style.width = '50%'
document.getElementById('animations').src =
'/copilot/static/martijn.gif'
setTimeout(function () {
document.getElementById('animations').src = ''
}, 5000)
}
if (res[1] == 'rick') {
document.getElementById('animations').style.width = '50%'
document.getElementById('animations').src =
'/copilot/static/rick.gif'
setTimeout(function () {
document.getElementById('animations').src = ''
}, 5000)
}
if (res[1] == 'true') {
document.getElementById('videoElement').style.width = '20%'
}
if (res[1] == 'false') {
document.getElementById('videoElement').style.width = '100%'
}
if (res[1].substring(0, 3) == 'htt') {
document.getElementById('iframe_main').src = res[1]
}
}
})
this.connection.addEventListener('close', function (event) {
console.log('The connection has been closed')
})
var animation1 = this.animation1
animation1()
}
})
</script>

View File

@ -147,6 +147,14 @@
type="text"
label="Title"
></q-input>
<q-select
filled
dense
emit-value
v-model="formDialogCopilot.data.wallet"
:options="g.user.walletOptions"
label="Wallet *"
></q-select>
<q-expansion-item
group="api"
@ -182,7 +190,7 @@
filled
dense
v-model.trim="formDialogCopilot.data.animation1webhook"
type="number"
type="text"
label="Webhook"
>
</q-input>
@ -218,6 +226,7 @@
v-model.trim="formDialogCopilot.data.animation2threshold"
type="number"
label="From *sats"
:rules="[ val <= formDialogCopilot.data.animation1threshold || 'Must be higher than last']"
>
</q-input>
</div>
@ -226,7 +235,7 @@
filled
dense
v-model.trim="formDialogCopilot.data.animation2webhook"
type="number"
type="text"
label="Webhook"
>
</q-input>
@ -262,6 +271,7 @@
v-model.trim="formDialogCopilot.data.animation3threshold"
type="number"
label="From *sats"
:rules="[ val <= formDialogCopilot.data.animation2threshold || 'Must be higher than last']"
>
</q-input>
</div>
@ -270,7 +280,7 @@
filled
dense
v-model.trim="formDialogCopilot.data.animation3webhook"
type="number"
type="text"
label="Webhook"
>
</q-input>
@ -409,20 +419,14 @@
data: {
show_message: false,
show_ack: true,
title: '',
animation1threshold: 0,
animation2threshold: 0,
animation3threshold: 0,
animation1webhook: '',
animation2webhook: '',
animation3webhook: ''
title: ''
}
},
qrCodeDialog: {
show: false,
data: null
},
options: ['moon_rocket', 'confetti', 'roller_coaster']
options: ['bitcoin', 'confetti', 'rocket', 'face', 'martijn', 'rick']
}
},
methods: {
@ -437,19 +441,15 @@
},
sendFormDataCopilot: function () {
var self = this
var wallet = this.g.user.wallets[0].adminkey
var data = this.formDialogCopilot.data
console.log(data)
data.animation1threshold = parseInt(data.animation1threshold)
data.animation1threshold = parseInt(data.animation2threshold)
data.animation1threshold = parseInt(data.animation3threshold)
this.createCopilot(wallet, data)
console.log(self.formDialogCopilot.data.animation1threshold)
this.createCopilot(
self.g.user.wallets[0].adminkey,
self.formDialogCopilot.data
)
},
createCopilot: function (wallet, data) {
var self = this
LNbits.api
.request('POST', '/copilot/api/v1/copilot', wallet, data)
.then(function (response) {
@ -477,7 +477,7 @@
},
opencopilotCompose: function (copilot_id) {
let params =
'scrollbars=no, resizable=no,status=no,location=no,toolbar=no,menubar=no,width=900,height=500,left=200,top=200'
'scrollbars=no, resizable=no,status=no,location=no,toolbar=no,menubar=no,width=1722,height=972,left=200,top=200'
open('../copilot/cp/' + copilot_id, 'test', params)
},

View File

@ -10,7 +10,7 @@
dense
@click="openCompose"
icon="face"
style="font-size: 100px"
style="font-size: 80px"
></q-btn>
</center>
</div>
@ -23,11 +23,11 @@
<div class="col">
<div class="row">
<div class="col">
<q-toggle
v-model="formDialogCopilot.fullscreen_cam"
size="xl"
icon="face"
label="Fullscreen"
<q-btn
class="q-mt-sm q-ml-sm"
color="primary"
@click="fullscreenToggle"
label="Webcam size"
/>
</div>
<div class="col">
@ -36,24 +36,71 @@
dense
outlined
bottom-slots
v-model="text"
v-model="iframe"
label="iframe url"
>
<template v-slot:after>
<q-btn round dense flat icon="send" />
<q-btn
round
dense
flat
@click="iframeChange(iframe)"
icon="send"
/>
</template>
</q-input>
</div>
</div>
<div class="row q-pa-sm">
<div class="col">
<q-btn color="primary" label="Animation 1" />
<q-btn
style="width: 95%"
color="primary"
@click="animationBTN('rocket')"
label="rocket"
/>
</div>
<div class="col">
<q-btn color="primary" label="Animation 2" />
<q-btn
style="width: 95%"
color="primary"
@click="animationBTN('confetti')"
label="confetti"
/>
</div>
<div class="col">
<q-btn color="primary" label="Animation 3" />
<q-btn
style="width: 95%"
color="primary"
@click="animationBTN('face')"
label="face"
/>
</div>
</div>
<div class="row q-pa-sm">
<div class="col">
<q-btn
style="width: 95%"
color="primary"
@click="animationBTN('rick')"
label="rick"
/>
</div>
<div class="col">
<q-btn
style="width: 95%"
color="primary"
@click="animationBTN('martijn')"
label="martijn"
/>
</div>
<div class="col">
<q-btn
style="width: 95%"
color="primary"
@click="animationBTN('bitcoin')"
label="bitcoin"
/>
</div>
</div>
<div class="column">
@ -65,9 +112,6 @@
clearable
type="textarea"
label="Notes"
:shadow-text="textareaShadowText"
@keydown="processTextareaFill"
@focus="processTextareaFill"
></q-input>
</div>
<div class="col">
@ -93,24 +137,67 @@
mixins: [windowMixin],
data() {
return {
newProgress: 0.4,
counter: 1,
newTimeLeft: '',
lnbtc: true,
onbtc: false,
formDialogCopilot: {
fullscreen_cam: true
}
fullscreen_cam: true,
textareaModel: '',
iframe: ''
}
},
methods: {
iframeChange: function (url) {
this.connection.send(String(url))
},
fullscreenToggle: function () {
console.log(this.fullscreen_cam)
this.connection.send(String(this.fullscreen_cam))
if (this.fullscreen_cam) {
this.fullscreen_cam = false
} else {
this.fullscreen_cam = true
}
},
openCompose: function () {
let params = `scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no,width=900,height=500,left=200,top=200`
let params = `scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no,width=1722,height=972,left=200,top=200`
open('../copilot/cp/{{ copilot.id }}', 'test', params)
},
animationBTN: function (name) {
this.connection.send(name)
},
stfu: function (name) {
this.connection.send('')
}
},
created: function () {}
created: function () {
console.log('{{ copilot.id }}')
if (location.protocol == 'https:') {
console.log(location.protocol)
this.connection = new WebSocket(
'wss://' +
document.domain +
':' +
location.port +
'/copilot/ws/panel/{{ copilot.id }}'
)
} else {
this.connection = new WebSocket(
'ws://' +
document.domain +
':' +
location.port +
'/copilot/ws/panel/{{ copilot.id }}'
)
}
this.connection.addEventListener('open', function (event) {})
this.connection.addEventListener('message', function (event) {
console.log('Message from server ', event.data)
})
this.connection.addEventListener('close', function (event) {
console.log('The connection has been closed')
})
}
})
</script>
{% endblock %}

View File

@ -6,11 +6,27 @@ from lnbits.decorators import check_user_exists, validate_uuids
from . import copilot_ext
from .crud import get_copilot
@copilot_ext.websocket('/ws')
async def ws():
from quart import g, abort, render_template, jsonify, websocket
from functools import wraps
import trio
import shortuuid
from . import copilot_ext
connected_websockets = {}
@copilot_ext.websocket('/ws/panel/<copilot_id>')
async def ws_panel(copilot_id):
global connected_websockets
while True:
data = await websocket.receive()
await websocket.send(f"echo {data}")
connected_websockets[copilot_id] = shortuuid.uuid() + '-' + data
@copilot_ext.websocket('/ws/compose/<copilot_id>')
async def ws_compose(copilot_id):
global connected_websockets
while True:
data = await websocket.receive()
await websocket.send(connected_websockets[copilot_id])
@copilot_ext.route("/")
@validate_uuids(["usr"], required=True)
@ -24,7 +40,7 @@ async def compose(copilot_id):
copilot = await get_copilot(copilot_id) or abort(
HTTPStatus.NOT_FOUND, "Copilot link does not exist."
)
return await render_template("copilot/compose.html", copilot=copilot)
return await render_template("copilot/compose.html", copilot=copilot, lnurl=copilot.lnurl)
@copilot_ext.route("/<copilot_id>")
async def panel(copilot_id):

View File

@ -6,6 +6,8 @@ import httpx
from lnbits.core.crud import get_user
from lnbits.decorators import api_check_wallet_key, api_validate_post_request
from . import copilot_ext
from lnbits.extensions.copilot import copilot_ext
from .crud import (
create_copilot,
@ -24,12 +26,13 @@ from .crud import (
@api_validate_post_request(
schema={
"title": {"type": "string", "empty": False, "required": True},
"wallet": {"type": "string", "empty": False, "required": True},
"animation1": {"type": "string", "required": False},
"animation2": {"type": "string", "required": False},
"animation3": {"type": "string", "required": False},
"animation1threshold": {"type": "integer", "required": False},
"animation2threshold": {"type": "integer", "required": False},
"animation3threshold": {"type": "integer", "required": False},
"animation1threshold": {"type": "string", "required": False},
"animation2threshold": {"type": "string", "required": False},
"animation3threshold": {"type": "string", "required": False},
"animation1webhook": {"type": "string", "required": False},
"animation2webhook": {"type": "string", "required": False},
"animation3webhook": {"type": "string", "required": False},