Merge remote-tracking branch 'origin/main' into main

This commit is contained in:
benarc 2022-03-07 04:26:40 +00:00
commit 72f2ec0be8
12 changed files with 89 additions and 38 deletions

View file

@ -84,18 +84,19 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
def check_funding_source(app: FastAPI) -> None: def check_funding_source(app: FastAPI) -> None:
@app.on_event("startup") @app.on_event("startup")
async def check_wallet_status(): async def check_wallet_status():
error_message, balance = await WALLET.status() while True:
if error_message: error_message, balance = await WALLET.status()
if not error_message:
break
warnings.warn( warnings.warn(
f" × The backend for {WALLET.__class__.__name__} isn't working properly: '{error_message}'", f" × The backend for {WALLET.__class__.__name__} isn't working properly: '{error_message}'",
RuntimeWarning, RuntimeWarning,
) )
print("Retrying connection to backend in 5 seconds...")
sys.exit(4) await asyncio.sleep(5)
else: print(
print( f" ✔️ {WALLET.__class__.__name__} seems to be connected and with a balance of {balance} msat."
f" ✔️ {WALLET.__class__.__name__} seems to be connected and with a balance of {balance} msat." )
)
def register_routes(app: FastAPI) -> None: def register_routes(app: FastAPI) -> None:

View file

@ -24,24 +24,24 @@ from lnbits.decorators import (
WalletTypeInfo, WalletTypeInfo,
get_key_type, get_key_type,
) )
from lnbits.helpers import url_for from lnbits.helpers import url_for, urlsafe_short_hash
from lnbits.requestvars import g from lnbits.requestvars import g
from lnbits.settings import LNBITS_ADMIN_USERS, LNBITS_SITE_TITLE
from lnbits.utils.exchange_rates import ( from lnbits.utils.exchange_rates import (
currencies, currencies,
fiat_amount_as_satoshis, fiat_amount_as_satoshis,
satoshis_amount_as_fiat, satoshis_amount_as_fiat,
) )
from lnbits.settings import LNBITS_SITE_TITLE
from .. import core_app, db from .. import core_app, db
from ..crud import ( from ..crud import (
create_payment,
get_payments, get_payments,
get_standalone_payment, get_standalone_payment,
save_balance_check,
update_wallet,
create_payment,
get_wallet, get_wallet,
save_balance_check,
update_payment_status, update_payment_status,
update_wallet,
) )
from ..services import ( from ..services import (
InvoiceFailure, InvoiceFailure,
@ -52,8 +52,6 @@ from ..services import (
perform_lnurlauth, perform_lnurlauth,
) )
from ..tasks import api_invoice_listeners from ..tasks import api_invoice_listeners
from lnbits.settings import LNBITS_ADMIN_USERS
from lnbits.helpers import urlsafe_short_hash
@core_app.get("/api/v1/wallet") @core_app.get("/api/v1/wallet")
@ -503,12 +501,13 @@ async def api_lnurlscan(code: str):
@core_app.post("/api/v1/payments/decode") @core_app.post("/api/v1/payments/decode")
async def api_payments_decode(data: str = Query(None)): async def api_payments_decode(data: str = Query(None)):
print(data)
try: try:
if data["data"][:5] == "LNURL": if data[:5] == "LNURL":
url = lnurl.decode(data["data"]) url = lnurl.decode(data)
return {"domain": url} return {"domain": url}
else: else:
invoice = bolt11.decode(data["data"]) invoice = bolt11.decode(data)
return { return {
"payment_hash": invoice.payment_hash, "payment_hash": invoice.payment_hash,
"amount_msat": invoice.amount_msat, "amount_msat": invoice.amount_msat,

View file

@ -88,7 +88,7 @@ async def get_copilot(copilot_id: str) -> Copilots:
async def get_copilots(user: str) -> List[Copilots]: async def get_copilots(user: str) -> List[Copilots]:
rows = await db.fetchall( rows = await db.fetchall(
"SELECT * FROM copilot.newer_copilots WHERE user = ?", (user,) 'SELECT * FROM copilot.newer_copilots WHERE "user" = ?', (user,)
) )
return [Copilots(**row) for row in rows] return [Copilots(**row) for row in rows]

View file

@ -76,7 +76,7 @@ async def delete_ticket(payment_hash: str) -> None:
async def delete_event_tickets(event_id: str) -> None: async def delete_event_tickets(event_id: str) -> None:
await db.execute("DELETE FROM events.tickets WHERE event = ?", (event_id,)) await db.execute("DELETE FROM events.ticket WHERE event = ?", (event_id,))
# EVENTS # EVENTS

View file

@ -12,7 +12,7 @@ async def create_jukebox(
juke_id = urlsafe_short_hash() juke_id = urlsafe_short_hash()
result = await db.execute( result = await db.execute(
""" """
INSERT INTO jukebox.jukebox (id, user, title, wallet, sp_user, sp_secret, sp_access_token, sp_refresh_token, sp_device, sp_playlists, price, profit) INSERT INTO jukebox.jukebox (id, "user", title, wallet, sp_user, sp_secret, sp_access_token, sp_refresh_token, sp_device, sp_playlists, price, profit)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", """,
( (
@ -41,6 +41,7 @@ async def update_jukebox(
q = ", ".join([f"{field[0]} = ?" for field in data]) q = ", ".join([f"{field[0]} = ?" for field in data])
items = [f"{field[1]}" for field in data] items = [f"{field[1]}" for field in data]
items.append(juke_id) items.append(juke_id)
q = q.replace("user", '"user"', 1) # hack to make user be "user"!
await db.execute(f"UPDATE jukebox.jukebox SET {q} WHERE id = ?", (items)) await db.execute(f"UPDATE jukebox.jukebox SET {q} WHERE id = ?", (items))
row = await db.fetchone("SELECT * FROM jukebox.jukebox WHERE id = ?", (juke_id,)) row = await db.fetchone("SELECT * FROM jukebox.jukebox WHERE id = ?", (juke_id,))
return Jukebox(**row) if row else None return Jukebox(**row) if row else None
@ -57,11 +58,11 @@ async def get_jukebox_by_user(user: str) -> Optional[Jukebox]:
async def get_jukeboxs(user: str) -> List[Jukebox]: async def get_jukeboxs(user: str) -> List[Jukebox]:
rows = await db.fetchall("SELECT * FROM jukebox.jukebox WHERE user = ?", (user,)) rows = await db.fetchall('SELECT * FROM jukebox.jukebox WHERE "user" = ?', (user,))
for row in rows: for row in rows:
if row.sp_playlists == None: if row.sp_playlists == None:
await delete_jukebox(row.id) await delete_jukebox(row.id)
rows = await db.fetchall("SELECT * FROM jukebox.jukebox WHERE user = ?", (user,)) rows = await db.fetchall('SELECT * FROM jukebox.jukebox WHERE "user" = ?', (user,))
return [Jukebox(**row) for row in rows] return [Jukebox(**row) for row in rows]

View file

@ -75,7 +75,6 @@ async def api_check_credentials_check(
juke_id: str = Query(None), wallet: WalletTypeInfo = Depends(require_admin_key) juke_id: str = Query(None), wallet: WalletTypeInfo = Depends(require_admin_key)
): ):
jukebox = await get_jukebox(juke_id) jukebox = await get_jukebox(juke_id)
return jukebox return jukebox
@ -442,7 +441,7 @@ async def api_get_jukebox_currently(
token = await api_get_token(juke_id) token = await api_get_token(juke_id)
if token == False: if token == False:
raise HTTPException( raise HTTPException(
status_code=HTTPStatus.FORBIDDEN, detail="INvoice not paid" status_code=HTTPStatus.FORBIDDEN, detail="Invoice not paid"
) )
elif retry: elif retry:
raise HTTPException( raise HTTPException(
@ -456,5 +455,5 @@ async def api_get_jukebox_currently(
) )
except: except:
raise HTTPException( raise HTTPException(
status_code=HTTPStatus.NOT_FOUND, detail="Something went wrong" status_code=HTTPStatus.NOT_FOUND, detail="Something went wrong, or no song is playing yet"
) )

View file

@ -117,9 +117,10 @@
{% raw %} {% raw %}
<template v-slot:header="props"> <template v-slot:header="props">
<q-tr :props="props"> <q-tr :props="props">
<q-th auto-width></q-th>
<q-th auto-width></q-th> <q-th auto-width></q-th>
<q-th v-for="col in props.cols" :key="col.name" :props="props"> <q-th v-for="col in props.cols" :key="col.name" :props="props">
{{ col.label }} {{ col.label }}
</q-th> </q-th>
</q-tr> </q-tr>
</template> </template>
@ -136,9 +137,19 @@
:href="'mailto:' + props.row.email" :href="'mailto:' + props.row.email"
></q-btn> ></q-btn>
</q-td> </q-td>
<q-td auto-width>
<q-btn
unelevated
dense
size="xs"
icon="launch"
:color="($q.dark.isActive) ? 'grey-7' : 'grey-5'"
@click="ticketCard(props)"
><q-tooltip> Click to show ticket </q-tooltip></q-btn>
</q-td>
<q-td v-for="col in props.cols" :key="col.name" :props="props"> <q-td v-for="col in props.cols" :key="col.name" :props="props">
{{ col.value }} {{ col.label == "Ticket" ? col.value.length > 20 ? `${col.value.substring(0, 20)}...` : col.value : col.value }}
</q-td> </q-td>
<q-td auto-width> <q-td auto-width>
@ -249,6 +260,29 @@
</q-form> </q-form>
</q-card> </q-card>
</q-dialog> </q-dialog>
<!-- Read Ticket Dialog -->
<q-dialog v-model="ticketDialog.show" position="top">
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
{% raw %}
<q-card-section>
<h4 class="text-subtitle1 q-my-none">
<i>{{this.ticketDialog.data.name}}</i> sent a ticket
</h4>
<div v-if="this.ticketDialog.data.email">
<small>{{this.ticketDialog.data.email}}</small>
</div>
<small>{{this.ticketDialog.data.date}}</small>
</q-card-section>
<q-separator></q-separator>
<q-card-section>
<p>{{this.ticketDialog.data.content}}</p>
</q-card-section>
{% endraw %}
<q-card-actions align="right">
<q-btn flat label="CLOSE" color="primary" v-close-popup />
</q-card-actions>
</q-card>
</q-dialog>
</div> </div>
{% endblock %} {% block scripts %} {{ window_vars(user) }} {% endblock %} {% block scripts %} {{ window_vars(user) }}
<script> <script>
@ -318,6 +352,10 @@
formDialog: { formDialog: {
show: false, show: false,
data: {flatrate: false} data: {flatrate: false}
},
ticketDialog: {
show: false,
data: {}
} }
} }
}, },
@ -372,6 +410,16 @@
}) })
}) })
}, },
ticketCard(ticket){
this.ticketDialog.show = true
let {date, email, ltext, name} = ticket.row
this.ticketDialog.data = {
date,
email,
content: ltext,
name
}
},
exportticketsCSV: function () { exportticketsCSV: function () {
LNbits.utils.exportCSV(this.ticketsTable.columns, this.tickets) LNbits.utils.exportCSV(this.ticketsTable.columns, this.tickets)
}, },
@ -421,12 +469,13 @@
}, },
updateformDialog: function (formId) { updateformDialog: function (formId) {
var link = _.findWhere(this.forms, {id: formId}) var link = _.findWhere(this.forms, {id: formId})
console.log("LINK", link)
this.formDialog.data.id = link.id this.formDialog.data.id = link.id
this.formDialog.data.wallet = link.wallet this.formDialog.data.wallet = link.wallet
this.formDialog.data.name = link.name this.formDialog.data.name = link.name
this.formDialog.data.description = link.description this.formDialog.data.description = link.description
this.formDialog.data.flatrate = link.flatrate this.formDialog.data.flatrate = Boolean(link.flatrate)
this.formDialog.data.amount = link.amount this.formDialog.data.amount = link.amount
this.formDialog.show = true this.formDialog.show = true
}, },

View file

@ -60,7 +60,7 @@ class Service(BaseModel):
onchain: Optional[str] onchain: Optional[str]
servicename: str # Currently, this will just always be "Streamlabs" servicename: str # Currently, this will just always be "Streamlabs"
authenticated: bool # Whether a token (see below) has been acquired yet authenticated: bool # Whether a token (see below) has been acquired yet
token: Optional[int] # The token with which to authenticate requests token: Optional[str] # The token with which to authenticate requests
@classmethod @classmethod
def from_row(cls, row: Row) -> "Service": def from_row(cls, row: Row) -> "Service":

View file

@ -62,7 +62,7 @@
donationDialog: { donationDialog: {
show: false, show: false,
data: { data: {
name: '', name: null,
sats: '', sats: '',
message: '' message: ''
} }

View file

@ -7,6 +7,7 @@ from starlette.responses import RedirectResponse
from lnbits.core.crud import get_user from lnbits.core.crud import get_user
from lnbits.decorators import WalletTypeInfo, get_key_type from lnbits.decorators import WalletTypeInfo, get_key_type
from lnbits.extensions.satspay.models import CreateCharge
from lnbits.extensions.streamalerts.models import ( from lnbits.extensions.streamalerts.models import (
CreateDonation, CreateDonation,
CreateService, CreateService,
@ -113,17 +114,18 @@ async def api_create_donation(data: CreateDonation, request: Request):
service_id = data.service service_id = data.service
service = await get_service(service_id) service = await get_service(service_id)
charge_details = await get_charge_details(service.id) charge_details = await get_charge_details(service.id)
name = data.name name = data.name if data.name else "Anonymous"
description = f"{sats} sats donation from {name} to {service.twitchuser}" description = f"{sats} sats donation from {name} to {service.twitchuser}"
charge = await create_charge( create_charge_data = CreateCharge(
amount=sats, amount=sats,
completelink=f"https://twitch.tv/{service.twitchuser}", completelink=f"https://twitch.tv/{service.twitchuser}",
completelinktext="Back to Stream!", completelinktext="Back to Stream!",
webhook=webhook_base + "/streamalerts/api/v1/postdonation", webhook=webhook_base + "/streamalerts/api/v1/postdonation",
description=description, description=description,
**charge_details, **charge_details
) )
charge = await create_charge(user=charge_details["user"], data=create_charge_data)
await create_donation( await create_donation(
id=charge.id, id=charge.id,
wallet=service.wallet, wallet=service.wallet,

View file

@ -1,5 +1,5 @@
from sqlite3 import Row from sqlite3 import Row
from typing import NamedTuple, Optional from typing import Optional
from fastapi.param_functions import Query from fastapi.param_functions import Query
from pydantic import BaseModel from pydantic import BaseModel
@ -26,7 +26,7 @@ class createTip(BaseModel):
message: str = "" message: str = ""
class Tip(NamedTuple): class Tip(BaseModel):
"""A Tip represents a single donation""" """A Tip represents a single donation"""
id: str # This ID always corresponds to a satspay charge ID id: str # This ID always corresponds to a satspay charge ID
@ -55,7 +55,7 @@ class createTips(BaseModel):
message: str message: str
class TipJar(NamedTuple): class TipJar(BaseModel):
"""A TipJar represents a user's tip jar""" """A TipJar represents a user's tip jar"""
id: int id: int

View file

@ -112,7 +112,7 @@ async def api_get_addresses(wallet_id, w: WalletTypeInfo = Depends(get_key_type)
async def api_update_mempool( async def api_update_mempool(
endpoint: str = Query(...), w: WalletTypeInfo = Depends(require_admin_key) endpoint: str = Query(...), w: WalletTypeInfo = Depends(require_admin_key)
): ):
mempool = await update_mempool(endpoint, user=w.wallet.user) mempool = await update_mempool(**{"endpoint": endpoint}, user=w.wallet.user)
return mempool.dict() return mempool.dict()