mirror of
https://github.com/lnbits/lnbits-legend.git
synced 2025-03-10 17:26:15 +01:00
Merge pull request #1505 from lnbits/removesatsdiceagain
Remove satsdice...again
This commit is contained in:
commit
ef3beaaccf
16 changed files with 0 additions and 1892 deletions
|
@ -1,5 +0,0 @@
|
||||||
# satsdice
|
|
||||||
|
|
||||||
## Create staic LNURL powered satsdices
|
|
||||||
|
|
||||||
Gambling is dangerous, flip responsibly
|
|
|
@ -1,26 +0,0 @@
|
||||||
from fastapi import APIRouter
|
|
||||||
from starlette.staticfiles import StaticFiles
|
|
||||||
|
|
||||||
from lnbits.db import Database
|
|
||||||
from lnbits.helpers import template_renderer
|
|
||||||
|
|
||||||
db = Database("ext_satsdice")
|
|
||||||
|
|
||||||
satsdice_ext: APIRouter = APIRouter(prefix="/satsdice", tags=["satsdice"])
|
|
||||||
|
|
||||||
satsdice_static_files = [
|
|
||||||
{
|
|
||||||
"path": "/satsdice/static",
|
|
||||||
"app": StaticFiles(directory="lnbits/extensions/satsdice/static"),
|
|
||||||
"name": "satsdice_static",
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def satsdice_renderer():
|
|
||||||
return template_renderer(["lnbits/extensions/satsdice/templates"])
|
|
||||||
|
|
||||||
|
|
||||||
from .lnurl import * # noqa: F401,F403
|
|
||||||
from .views import * # noqa: F401,F403
|
|
||||||
from .views_api import * # noqa: F401,F403
|
|
|
@ -1,6 +0,0 @@
|
||||||
{
|
|
||||||
"name": "Sats Dice",
|
|
||||||
"short_description": "LNURL Satoshi dice",
|
|
||||||
"tile": "/satsdice/static/image/satsdice.png",
|
|
||||||
"contributors": ["arcbtc"]
|
|
||||||
}
|
|
|
@ -1,279 +0,0 @@
|
||||||
from datetime import datetime
|
|
||||||
from typing import List, Optional, Union
|
|
||||||
|
|
||||||
from lnbits.helpers import urlsafe_short_hash
|
|
||||||
|
|
||||||
from . import db
|
|
||||||
from .models import (
|
|
||||||
CreateSatsDiceLink,
|
|
||||||
CreateSatsDicePayment,
|
|
||||||
CreateSatsDiceWithdraw,
|
|
||||||
satsdiceLink,
|
|
||||||
satsdicePayment,
|
|
||||||
satsdiceWithdraw,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def create_satsdice_pay(wallet_id: str, data: CreateSatsDiceLink) -> satsdiceLink:
|
|
||||||
satsdice_id = urlsafe_short_hash()
|
|
||||||
await db.execute(
|
|
||||||
"""
|
|
||||||
INSERT INTO satsdice.satsdice_pay (
|
|
||||||
id,
|
|
||||||
wallet,
|
|
||||||
title,
|
|
||||||
base_url,
|
|
||||||
min_bet,
|
|
||||||
max_bet,
|
|
||||||
amount,
|
|
||||||
served_meta,
|
|
||||||
served_pr,
|
|
||||||
multiplier,
|
|
||||||
chance,
|
|
||||||
haircut,
|
|
||||||
open_time
|
|
||||||
)
|
|
||||||
VALUES (?, ?, ?, ?, ?, ?, 0, 0, 0, ?, ?, ?, ?)
|
|
||||||
""",
|
|
||||||
(
|
|
||||||
satsdice_id,
|
|
||||||
wallet_id,
|
|
||||||
data.title,
|
|
||||||
data.base_url,
|
|
||||||
data.min_bet,
|
|
||||||
data.max_bet,
|
|
||||||
data.multiplier,
|
|
||||||
data.chance,
|
|
||||||
data.haircut,
|
|
||||||
int(datetime.now().timestamp()),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
link = await get_satsdice_pay(satsdice_id)
|
|
||||||
assert link, "Newly created link couldn't be retrieved"
|
|
||||||
return link
|
|
||||||
|
|
||||||
|
|
||||||
async def get_satsdice_pay(link_id: str) -> Optional[satsdiceLink]:
|
|
||||||
row = await db.fetchone(
|
|
||||||
"SELECT * FROM satsdice.satsdice_pay WHERE id = ?", (link_id,)
|
|
||||||
)
|
|
||||||
return satsdiceLink(**row) if row else None
|
|
||||||
|
|
||||||
|
|
||||||
async def get_satsdice_pays(wallet_ids: Union[str, List[str]]) -> List[satsdiceLink]:
|
|
||||||
if isinstance(wallet_ids, str):
|
|
||||||
wallet_ids = [wallet_ids]
|
|
||||||
|
|
||||||
q = ",".join(["?"] * len(wallet_ids))
|
|
||||||
rows = await db.fetchall(
|
|
||||||
f"""
|
|
||||||
SELECT * FROM satsdice.satsdice_pay WHERE wallet IN ({q})
|
|
||||||
ORDER BY id
|
|
||||||
""",
|
|
||||||
(*wallet_ids,),
|
|
||||||
)
|
|
||||||
return [satsdiceLink(**row) for row in rows]
|
|
||||||
|
|
||||||
|
|
||||||
async def update_satsdice_pay(link_id: str, **kwargs) -> satsdiceLink:
|
|
||||||
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
|
|
||||||
await db.execute(
|
|
||||||
f"UPDATE satsdice.satsdice_pay SET {q} WHERE id = ?",
|
|
||||||
(*kwargs.values(), link_id),
|
|
||||||
)
|
|
||||||
row = await db.fetchone(
|
|
||||||
"SELECT * FROM satsdice.satsdice_pay WHERE id = ?", (link_id,)
|
|
||||||
)
|
|
||||||
return satsdiceLink(**row)
|
|
||||||
|
|
||||||
|
|
||||||
async def increment_satsdice_pay(link_id: str, **kwargs) -> Optional[satsdiceLink]:
|
|
||||||
q = ", ".join([f"{field[0]} = {field[0]} + ?" for field in kwargs.items()])
|
|
||||||
await db.execute(
|
|
||||||
f"UPDATE satsdice.satsdice_pay SET {q} WHERE id = ?",
|
|
||||||
(*kwargs.values(), link_id),
|
|
||||||
)
|
|
||||||
row = await db.fetchone(
|
|
||||||
"SELECT * FROM satsdice.satsdice_pay WHERE id = ?", (link_id,)
|
|
||||||
)
|
|
||||||
return satsdiceLink(**row) if row else None
|
|
||||||
|
|
||||||
|
|
||||||
async def delete_satsdice_pay(link_id: str) -> None:
|
|
||||||
await db.execute("DELETE FROM satsdice.satsdice_pay WHERE id = ?", (link_id,))
|
|
||||||
|
|
||||||
|
|
||||||
##################SATSDICE PAYMENT LINKS
|
|
||||||
|
|
||||||
|
|
||||||
async def create_satsdice_payment(data: CreateSatsDicePayment) -> satsdicePayment:
|
|
||||||
await db.execute(
|
|
||||||
"""
|
|
||||||
INSERT INTO satsdice.satsdice_payment (
|
|
||||||
payment_hash,
|
|
||||||
satsdice_pay,
|
|
||||||
value,
|
|
||||||
paid,
|
|
||||||
lost
|
|
||||||
)
|
|
||||||
VALUES (?, ?, ?, ?, ?)
|
|
||||||
""",
|
|
||||||
(
|
|
||||||
data.payment_hash,
|
|
||||||
data.satsdice_pay,
|
|
||||||
data.value,
|
|
||||||
False,
|
|
||||||
False,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
payment = await get_satsdice_payment(data.payment_hash)
|
|
||||||
assert payment, "Newly created withdraw couldn't be retrieved"
|
|
||||||
return payment
|
|
||||||
|
|
||||||
|
|
||||||
async def get_satsdice_payment(payment_hash: str) -> Optional[satsdicePayment]:
|
|
||||||
row = await db.fetchone(
|
|
||||||
"SELECT * FROM satsdice.satsdice_payment WHERE payment_hash = ?",
|
|
||||||
(payment_hash,),
|
|
||||||
)
|
|
||||||
return satsdicePayment(**row) if row else None
|
|
||||||
|
|
||||||
|
|
||||||
async def update_satsdice_payment(payment_hash: str, **kwargs) -> satsdicePayment:
|
|
||||||
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
|
|
||||||
|
|
||||||
await db.execute(
|
|
||||||
f"UPDATE satsdice.satsdice_payment SET {q} WHERE payment_hash = ?",
|
|
||||||
(bool(*kwargs.values()), payment_hash),
|
|
||||||
)
|
|
||||||
row = await db.fetchone(
|
|
||||||
"SELECT * FROM satsdice.satsdice_payment WHERE payment_hash = ?",
|
|
||||||
(payment_hash,),
|
|
||||||
)
|
|
||||||
return satsdicePayment(**row)
|
|
||||||
|
|
||||||
|
|
||||||
##################SATSDICE WITHDRAW LINKS
|
|
||||||
|
|
||||||
|
|
||||||
async def create_satsdice_withdraw(data: CreateSatsDiceWithdraw) -> satsdiceWithdraw:
|
|
||||||
await db.execute(
|
|
||||||
"""
|
|
||||||
INSERT INTO satsdice.satsdice_withdraw (
|
|
||||||
id,
|
|
||||||
satsdice_pay,
|
|
||||||
value,
|
|
||||||
unique_hash,
|
|
||||||
k1,
|
|
||||||
open_time,
|
|
||||||
used
|
|
||||||
)
|
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
||||||
""",
|
|
||||||
(
|
|
||||||
data.payment_hash,
|
|
||||||
data.satsdice_pay,
|
|
||||||
data.value,
|
|
||||||
urlsafe_short_hash(),
|
|
||||||
urlsafe_short_hash(),
|
|
||||||
int(datetime.now().timestamp()),
|
|
||||||
data.used,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
withdraw = await get_satsdice_withdraw(data.payment_hash, 0)
|
|
||||||
assert withdraw, "Newly created withdraw couldn't be retrieved"
|
|
||||||
return withdraw
|
|
||||||
|
|
||||||
|
|
||||||
async def get_satsdice_withdraw(withdraw_id: str, num=0) -> Optional[satsdiceWithdraw]:
|
|
||||||
row = await db.fetchone(
|
|
||||||
"SELECT * FROM satsdice.satsdice_withdraw WHERE id = ?", (withdraw_id,)
|
|
||||||
)
|
|
||||||
if not row:
|
|
||||||
return None
|
|
||||||
|
|
||||||
withdraw = []
|
|
||||||
for item in row:
|
|
||||||
withdraw.append(item)
|
|
||||||
withdraw.append(num)
|
|
||||||
return satsdiceWithdraw(**row)
|
|
||||||
|
|
||||||
|
|
||||||
async def get_satsdice_withdraw_by_hash(
|
|
||||||
unique_hash: str, num=0
|
|
||||||
) -> Optional[satsdiceWithdraw]:
|
|
||||||
row = await db.fetchone(
|
|
||||||
"SELECT * FROM satsdice.satsdice_withdraw WHERE unique_hash = ?", (unique_hash,)
|
|
||||||
)
|
|
||||||
if not row:
|
|
||||||
return None
|
|
||||||
|
|
||||||
withdraw = []
|
|
||||||
for item in row:
|
|
||||||
withdraw.append(item)
|
|
||||||
withdraw.append(num)
|
|
||||||
return satsdiceWithdraw(**row)
|
|
||||||
|
|
||||||
|
|
||||||
async def get_satsdice_withdraws(
|
|
||||||
wallet_ids: Union[str, List[str]]
|
|
||||||
) -> List[satsdiceWithdraw]:
|
|
||||||
if isinstance(wallet_ids, str):
|
|
||||||
wallet_ids = [wallet_ids]
|
|
||||||
|
|
||||||
q = ",".join(["?"] * len(wallet_ids))
|
|
||||||
rows = await db.fetchall(
|
|
||||||
f"SELECT * FROM satsdice.satsdice_withdraw WHERE wallet IN ({q})",
|
|
||||||
(*wallet_ids,),
|
|
||||||
)
|
|
||||||
|
|
||||||
return [satsdiceWithdraw(**row) for row in rows]
|
|
||||||
|
|
||||||
|
|
||||||
async def update_satsdice_withdraw(
|
|
||||||
withdraw_id: str, **kwargs
|
|
||||||
) -> Optional[satsdiceWithdraw]:
|
|
||||||
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
|
|
||||||
await db.execute(
|
|
||||||
f"UPDATE satsdice.satsdice_withdraw SET {q} WHERE id = ?",
|
|
||||||
(*kwargs.values(), withdraw_id),
|
|
||||||
)
|
|
||||||
row = await db.fetchone(
|
|
||||||
"SELECT * FROM satsdice.satsdice_withdraw WHERE id = ?", (withdraw_id,)
|
|
||||||
)
|
|
||||||
return satsdiceWithdraw(**row) if row else None
|
|
||||||
|
|
||||||
|
|
||||||
async def delete_satsdice_withdraw(withdraw_id: str) -> None:
|
|
||||||
await db.execute(
|
|
||||||
"DELETE FROM satsdice.satsdice_withdraw WHERE id = ?", (withdraw_id,)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def create_withdraw_hash_check(the_hash: str, lnurl_id: str):
|
|
||||||
await db.execute(
|
|
||||||
"""
|
|
||||||
INSERT INTO satsdice.hash_checkw (
|
|
||||||
id,
|
|
||||||
lnurl_id
|
|
||||||
)
|
|
||||||
VALUES (?, ?)
|
|
||||||
""",
|
|
||||||
(the_hash, lnurl_id),
|
|
||||||
)
|
|
||||||
hashCheck = await get_withdraw_hash_checkw(the_hash, lnurl_id)
|
|
||||||
return hashCheck
|
|
||||||
|
|
||||||
|
|
||||||
async def get_withdraw_hash_checkw(the_hash: str, lnurl_id: str):
|
|
||||||
rowid = await db.fetchone(
|
|
||||||
"SELECT * FROM satsdice.hash_checkw WHERE id = ?", (the_hash,)
|
|
||||||
)
|
|
||||||
rowlnurl = await db.fetchone(
|
|
||||||
"SELECT * FROM satsdice.hash_checkw WHERE lnurl_id = ?", (lnurl_id,)
|
|
||||||
)
|
|
||||||
if not rowlnurl or not rowid:
|
|
||||||
await create_withdraw_hash_check(the_hash, lnurl_id)
|
|
||||||
return {"lnurl": True, "hash": False}
|
|
||||||
else:
|
|
||||||
return {"lnurl": True, "hash": True}
|
|
|
@ -1,156 +0,0 @@
|
||||||
import json
|
|
||||||
import math
|
|
||||||
from http import HTTPStatus
|
|
||||||
|
|
||||||
from fastapi import Request
|
|
||||||
from fastapi.param_functions import Query
|
|
||||||
from starlette.exceptions import HTTPException
|
|
||||||
from starlette.responses import HTMLResponse
|
|
||||||
|
|
||||||
from lnbits.core.services import create_invoice, pay_invoice
|
|
||||||
|
|
||||||
from . import satsdice_ext
|
|
||||||
from .crud import (
|
|
||||||
create_satsdice_payment,
|
|
||||||
get_satsdice_pay,
|
|
||||||
get_satsdice_withdraw_by_hash,
|
|
||||||
update_satsdice_withdraw,
|
|
||||||
)
|
|
||||||
from .models import CreateSatsDicePayment
|
|
||||||
|
|
||||||
|
|
||||||
@satsdice_ext.get(
|
|
||||||
"/api/v1/lnurlp/{link_id}",
|
|
||||||
response_class=HTMLResponse,
|
|
||||||
name="satsdice.lnurlp_response",
|
|
||||||
)
|
|
||||||
async def api_lnurlp_response(req: Request, link_id: str = Query(None)):
|
|
||||||
link = await get_satsdice_pay(link_id)
|
|
||||||
if not link:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=HTTPStatus.NOT_FOUND, detail="LNURL-pay not found."
|
|
||||||
)
|
|
||||||
payResponse = {
|
|
||||||
"tag": "payRequest",
|
|
||||||
"callback": req.url_for("satsdice.api_lnurlp_callback", link_id=link.id),
|
|
||||||
"metadata": link.lnurlpay_metadata,
|
|
||||||
"minSendable": math.ceil(link.min_bet * 1) * 1000,
|
|
||||||
"maxSendable": round(link.max_bet * 1) * 1000,
|
|
||||||
}
|
|
||||||
return json.dumps(payResponse)
|
|
||||||
|
|
||||||
|
|
||||||
@satsdice_ext.get(
|
|
||||||
"/api/v1/lnurlp/cb/{link_id}",
|
|
||||||
response_class=HTMLResponse,
|
|
||||||
name="satsdice.api_lnurlp_callback",
|
|
||||||
)
|
|
||||||
async def api_lnurlp_callback(
|
|
||||||
req: Request, link_id: str = Query(None), amount: str = Query(None)
|
|
||||||
):
|
|
||||||
link = await get_satsdice_pay(link_id)
|
|
||||||
if not link:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=HTTPStatus.NOT_FOUND, detail="LNURL-pay not found."
|
|
||||||
)
|
|
||||||
|
|
||||||
min, max = link.min_bet, link.max_bet
|
|
||||||
min = link.min_bet * 1000
|
|
||||||
max = link.max_bet * 1000
|
|
||||||
|
|
||||||
amount_received = int(amount or 0)
|
|
||||||
if amount_received < min:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=HTTPStatus.FORBIDDEN,
|
|
||||||
detail=f"Amount {amount_received} is smaller than minimum {min}.",
|
|
||||||
)
|
|
||||||
elif amount_received > max:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=HTTPStatus.FORBIDDEN,
|
|
||||||
detail=f"Amount {amount_received} is greater than maximum {max}.",
|
|
||||||
)
|
|
||||||
|
|
||||||
payment_hash, payment_request = await create_invoice(
|
|
||||||
wallet_id=link.wallet,
|
|
||||||
amount=int(amount_received / 1000),
|
|
||||||
memo="Satsdice bet",
|
|
||||||
unhashed_description=link.lnurlpay_metadata.encode(),
|
|
||||||
extra={"tag": "satsdice", "link": link.id, "comment": "comment"},
|
|
||||||
)
|
|
||||||
|
|
||||||
success_action = link.success_action(payment_hash=payment_hash, req=req)
|
|
||||||
|
|
||||||
data = CreateSatsDicePayment(
|
|
||||||
satsdice_pay=link.id,
|
|
||||||
value=int(amount_received / 1000),
|
|
||||||
payment_hash=payment_hash,
|
|
||||||
)
|
|
||||||
|
|
||||||
await create_satsdice_payment(data)
|
|
||||||
payResponse: dict = {
|
|
||||||
"pr": payment_request,
|
|
||||||
"successAction": success_action,
|
|
||||||
"routes": [],
|
|
||||||
}
|
|
||||||
return json.dumps(payResponse)
|
|
||||||
|
|
||||||
|
|
||||||
##############LNURLW STUFF
|
|
||||||
|
|
||||||
|
|
||||||
@satsdice_ext.get(
|
|
||||||
"/api/v1/lnurlw/{unique_hash}",
|
|
||||||
response_class=HTMLResponse,
|
|
||||||
name="satsdice.lnurlw_response",
|
|
||||||
)
|
|
||||||
async def api_lnurlw_response(req: Request, unique_hash: str = Query(None)):
|
|
||||||
link = await get_satsdice_withdraw_by_hash(unique_hash)
|
|
||||||
|
|
||||||
if not link:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=HTTPStatus.NOT_FOUND, detail="LNURL-satsdice not found."
|
|
||||||
)
|
|
||||||
if link.used:
|
|
||||||
raise HTTPException(status_code=HTTPStatus.OK, detail="satsdice is spent.")
|
|
||||||
url = req.url_for("satsdice.api_lnurlw_callback", unique_hash=link.unique_hash)
|
|
||||||
withdrawResponse = {
|
|
||||||
"tag": "withdrawRequest",
|
|
||||||
"callback": url,
|
|
||||||
"k1": link.k1,
|
|
||||||
"minWithdrawable": link.value * 1000,
|
|
||||||
"maxWithdrawable": link.value * 1000,
|
|
||||||
"defaultDescription": "Satsdice winnings!",
|
|
||||||
}
|
|
||||||
return json.dumps(withdrawResponse)
|
|
||||||
|
|
||||||
|
|
||||||
# CALLBACK
|
|
||||||
|
|
||||||
|
|
||||||
@satsdice_ext.get(
|
|
||||||
"/api/v1/lnurlw/cb/{unique_hash}",
|
|
||||||
status_code=HTTPStatus.OK,
|
|
||||||
name="satsdice.api_lnurlw_callback",
|
|
||||||
)
|
|
||||||
async def api_lnurlw_callback(
|
|
||||||
unique_hash: str = Query(None),
|
|
||||||
pr: str = Query(None),
|
|
||||||
):
|
|
||||||
|
|
||||||
link = await get_satsdice_withdraw_by_hash(unique_hash)
|
|
||||||
if not link:
|
|
||||||
return {"status": "ERROR", "reason": "no withdraw"}
|
|
||||||
if link.used:
|
|
||||||
return {"status": "ERROR", "reason": "spent"}
|
|
||||||
paylink = await get_satsdice_pay(link.satsdice_pay)
|
|
||||||
|
|
||||||
if paylink:
|
|
||||||
await update_satsdice_withdraw(link.id, used=1)
|
|
||||||
await pay_invoice(
|
|
||||||
wallet_id=paylink.wallet,
|
|
||||||
payment_request=pr,
|
|
||||||
max_sat=link.value,
|
|
||||||
extra={"tag": "withdraw"},
|
|
||||||
)
|
|
||||||
|
|
||||||
return {"status": "OK"}
|
|
|
@ -1,73 +0,0 @@
|
||||||
async def m001_initial(db):
|
|
||||||
"""
|
|
||||||
Creates an improved satsdice table and migrates the existing data.
|
|
||||||
"""
|
|
||||||
await db.execute(
|
|
||||||
f"""
|
|
||||||
CREATE TABLE satsdice.satsdice_pay (
|
|
||||||
id TEXT PRIMARY KEY,
|
|
||||||
wallet TEXT,
|
|
||||||
title TEXT,
|
|
||||||
min_bet INTEGER,
|
|
||||||
max_bet INTEGER,
|
|
||||||
amount {db.big_int} DEFAULT 0,
|
|
||||||
served_meta INTEGER NOT NULL,
|
|
||||||
served_pr INTEGER NOT NULL,
|
|
||||||
multiplier FLOAT,
|
|
||||||
haircut FLOAT,
|
|
||||||
chance FLOAT,
|
|
||||||
base_url TEXT,
|
|
||||||
open_time INTEGER
|
|
||||||
);
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def m002_initial(db):
|
|
||||||
"""
|
|
||||||
Creates an improved satsdice table and migrates the existing data.
|
|
||||||
"""
|
|
||||||
await db.execute(
|
|
||||||
f"""
|
|
||||||
CREATE TABLE satsdice.satsdice_withdraw (
|
|
||||||
id TEXT PRIMARY KEY,
|
|
||||||
satsdice_pay TEXT,
|
|
||||||
value {db.big_int} DEFAULT 1,
|
|
||||||
unique_hash TEXT UNIQUE,
|
|
||||||
k1 TEXT,
|
|
||||||
open_time INTEGER,
|
|
||||||
used INTEGER DEFAULT 0
|
|
||||||
);
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def m003_initial(db):
|
|
||||||
"""
|
|
||||||
Creates an improved satsdice table and migrates the existing data.
|
|
||||||
"""
|
|
||||||
await db.execute(
|
|
||||||
f"""
|
|
||||||
CREATE TABLE satsdice.satsdice_payment (
|
|
||||||
payment_hash TEXT PRIMARY KEY,
|
|
||||||
satsdice_pay TEXT,
|
|
||||||
value {db.big_int},
|
|
||||||
paid BOOL DEFAULT FALSE,
|
|
||||||
lost BOOL DEFAULT FALSE
|
|
||||||
);
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def m004_make_hash_check(db):
|
|
||||||
"""
|
|
||||||
Creates a hash check table.
|
|
||||||
"""
|
|
||||||
await db.execute(
|
|
||||||
"""
|
|
||||||
CREATE TABLE satsdice.hash_checkw (
|
|
||||||
id TEXT PRIMARY KEY,
|
|
||||||
lnurl_id TEXT
|
|
||||||
);
|
|
||||||
"""
|
|
||||||
)
|
|
|
@ -1,134 +0,0 @@
|
||||||
import json
|
|
||||||
from sqlite3 import Row
|
|
||||||
from typing import Dict, Optional
|
|
||||||
|
|
||||||
from fastapi import Request
|
|
||||||
from fastapi.param_functions import Query
|
|
||||||
from lnurl import Lnurl
|
|
||||||
from lnurl import encode as lnurl_encode
|
|
||||||
from lnurl.types import LnurlPayMetadata
|
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
|
|
||||||
class satsdiceLink(BaseModel):
|
|
||||||
id: str
|
|
||||||
wallet: str
|
|
||||||
title: str
|
|
||||||
min_bet: int
|
|
||||||
max_bet: int
|
|
||||||
amount: int
|
|
||||||
served_meta: int
|
|
||||||
served_pr: int
|
|
||||||
multiplier: float
|
|
||||||
haircut: float
|
|
||||||
chance: float
|
|
||||||
base_url: str
|
|
||||||
open_time: int
|
|
||||||
|
|
||||||
def lnurl(self, req: Request) -> str:
|
|
||||||
return lnurl_encode(req.url_for("satsdice.lnurlp_response", link_id=self.id))
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_row(cls, row: Row) -> "satsdiceLink":
|
|
||||||
data = dict(row)
|
|
||||||
return cls(**data)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def lnurlpay_metadata(self) -> LnurlPayMetadata:
|
|
||||||
return LnurlPayMetadata(
|
|
||||||
json.dumps(
|
|
||||||
[
|
|
||||||
[
|
|
||||||
"text/plain",
|
|
||||||
f"{self.title} (Chance: {self.chance}%, Multiplier: {self.multiplier})",
|
|
||||||
]
|
|
||||||
]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def success_action(self, payment_hash: str, req: Request) -> Optional[Dict]:
|
|
||||||
url = req.url_for(
|
|
||||||
"satsdice.displaywin", link_id=self.id, payment_hash=payment_hash
|
|
||||||
)
|
|
||||||
return {"tag": "url", "description": "Check the attached link", "url": url}
|
|
||||||
|
|
||||||
|
|
||||||
class satsdicePayment(BaseModel):
|
|
||||||
payment_hash: str
|
|
||||||
satsdice_pay: str
|
|
||||||
value: int
|
|
||||||
paid: bool
|
|
||||||
lost: bool
|
|
||||||
|
|
||||||
|
|
||||||
class satsdiceWithdraw(BaseModel):
|
|
||||||
id: str
|
|
||||||
satsdice_pay: str
|
|
||||||
value: int
|
|
||||||
unique_hash: str
|
|
||||||
k1: str
|
|
||||||
open_time: int
|
|
||||||
used: int
|
|
||||||
|
|
||||||
def lnurl(self, req: Request) -> Lnurl:
|
|
||||||
return lnurl_encode(
|
|
||||||
req.url_for("satsdice.lnurlw_response", unique_hash=self.unique_hash)
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_spent(self) -> bool:
|
|
||||||
return self.used >= 1
|
|
||||||
|
|
||||||
def lnurl_response(self, req: Request):
|
|
||||||
url = req.url_for("satsdice.api_lnurlw_callback", unique_hash=self.unique_hash)
|
|
||||||
withdrawResponse = {
|
|
||||||
"tag": "withdrawRequest",
|
|
||||||
"callback": url,
|
|
||||||
"k1": self.k1,
|
|
||||||
"minWithdrawable": self.value * 1000,
|
|
||||||
"maxWithdrawable": self.value * 1000,
|
|
||||||
"defaultDescription": "Satsdice winnings!",
|
|
||||||
}
|
|
||||||
return withdrawResponse
|
|
||||||
|
|
||||||
|
|
||||||
class HashCheck(BaseModel):
|
|
||||||
id: str
|
|
||||||
lnurl_id: str
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_row(cls, row: Row):
|
|
||||||
return cls(**dict(row))
|
|
||||||
|
|
||||||
|
|
||||||
class CreateSatsDiceLink(BaseModel):
|
|
||||||
wallet: str = Query(None)
|
|
||||||
title: str = Query(None)
|
|
||||||
base_url: str = Query(None)
|
|
||||||
min_bet: str = Query(None)
|
|
||||||
max_bet: str = Query(None)
|
|
||||||
multiplier: float = Query(0)
|
|
||||||
chance: float = Query(0)
|
|
||||||
haircut: int = Query(0)
|
|
||||||
|
|
||||||
|
|
||||||
class CreateSatsDicePayment(BaseModel):
|
|
||||||
satsdice_pay: str = Query(None)
|
|
||||||
value: int = Query(0)
|
|
||||||
payment_hash: str = Query(None)
|
|
||||||
|
|
||||||
|
|
||||||
class CreateSatsDiceWithdraw(BaseModel):
|
|
||||||
payment_hash: str = Query(None)
|
|
||||||
satsdice_pay: str = Query(None)
|
|
||||||
value: int = Query(0)
|
|
||||||
used: int = Query(0)
|
|
||||||
|
|
||||||
|
|
||||||
class CreateSatsDiceWithdraws(BaseModel):
|
|
||||||
title: str = Query(None)
|
|
||||||
min_satsdiceable: int = Query(0)
|
|
||||||
max_satsdiceable: int = Query(0)
|
|
||||||
uses: int = Query(0)
|
|
||||||
wait_time: str = Query(None)
|
|
||||||
is_unique: bool = Query(False)
|
|
Binary file not shown.
Before Width: | Height: | Size: 22 KiB |
|
@ -1,198 +0,0 @@
|
||||||
<q-expansion-item
|
|
||||||
group="extras"
|
|
||||||
icon="swap_vertical_circle"
|
|
||||||
label="API info"
|
|
||||||
:content-inset-level="0.5"
|
|
||||||
>
|
|
||||||
<q-btn flat label="Swagger API" type="a" href="../docs#/satsdice"></q-btn>
|
|
||||||
<q-expansion-item group="api" dense expand-separator label="List satsdices">
|
|
||||||
<q-card>
|
|
||||||
<q-card-section>
|
|
||||||
<code><span class="text-blue">GET</span> /satsdice/api/v1/links</code>
|
|
||||||
<h5 class="text-caption q-mt-sm q-mb-none">Headers</h5>
|
|
||||||
<code>{"X-Api-Key": <invoice_key>}</code><br />
|
|
||||||
<h5 class="text-caption q-mt-sm q-mb-none">Body (application/json)</h5>
|
|
||||||
<h5 class="text-caption q-mt-sm q-mb-none">
|
|
||||||
Returns 200 OK (application/json)
|
|
||||||
</h5>
|
|
||||||
<code>[<satsdice_link_object>, ...]</code>
|
|
||||||
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
|
|
||||||
<code
|
|
||||||
>curl -X GET {{ request.base_url }}satsdice/api/v1/links -H
|
|
||||||
"X-Api-Key: {{ user.wallets[0].inkey }}"
|
|
||||||
</code>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</q-expansion-item>
|
|
||||||
<q-expansion-item
|
|
||||||
group="api"
|
|
||||||
dense
|
|
||||||
expand-separator
|
|
||||||
label="Get a satsdice link"
|
|
||||||
>
|
|
||||||
<q-card>
|
|
||||||
<q-card-section>
|
|
||||||
<code
|
|
||||||
><span class="text-blue">GET</span>
|
|
||||||
/satsdice/api/v1/links/<satsdice_id></code
|
|
||||||
>
|
|
||||||
<h5 class="text-caption q-mt-sm q-mb-none">Headers</h5>
|
|
||||||
<code>{"X-Api-Key": <invoice_key>}</code><br />
|
|
||||||
<h5 class="text-caption q-mt-sm q-mb-none">Body (application/json)</h5>
|
|
||||||
<h5 class="text-caption q-mt-sm q-mb-none">
|
|
||||||
Returns 201 CREATED (application/json)
|
|
||||||
</h5>
|
|
||||||
<code>{"lnurl": <string>}</code>
|
|
||||||
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
|
|
||||||
<code
|
|
||||||
>curl -X GET {{ request.base_url
|
|
||||||
}}satsdice/api/v1/links/<satsdice_id> -H "X-Api-Key: {{
|
|
||||||
user.wallets[0].inkey }}"
|
|
||||||
</code>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</q-expansion-item>
|
|
||||||
<q-expansion-item
|
|
||||||
group="api"
|
|
||||||
dense
|
|
||||||
expand-separator
|
|
||||||
label="Create a satsdice link"
|
|
||||||
>
|
|
||||||
<q-card>
|
|
||||||
<q-card-section>
|
|
||||||
<code><span class="text-green">POST</span> /satsdice/api/v1/links</code>
|
|
||||||
<h5 class="text-caption q-mt-sm q-mb-none">Headers</h5>
|
|
||||||
<code>{"X-Api-Key": <admin_key>}</code><br />
|
|
||||||
<h5 class="text-caption q-mt-sm q-mb-none">Body (application/json)</h5>
|
|
||||||
<code
|
|
||||||
>{"title": <string>, "min_satsdiceable": <integer>,
|
|
||||||
"max_satsdiceable": <integer>, "uses": <integer>,
|
|
||||||
"wait_time": <integer>, "is_unique": <boolean>}</code
|
|
||||||
>
|
|
||||||
<h5 class="text-caption q-mt-sm q-mb-none">
|
|
||||||
Returns 201 CREATED (application/json)
|
|
||||||
</h5>
|
|
||||||
<code>{"lnurl": <string>}</code>
|
|
||||||
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
|
|
||||||
<code
|
|
||||||
>curl -X POST {{ request.base_url }}satsdice/api/v1/links -d
|
|
||||||
'{"title": <string>, "min_satsdiceable": <integer>,
|
|
||||||
"max_satsdiceable": <integer>, "uses": <integer>,
|
|
||||||
"wait_time": <integer>, "is_unique": <boolean>}' -H
|
|
||||||
"Content-type: application/json" -H "X-Api-Key: {{
|
|
||||||
user.wallets[0].adminkey }}"
|
|
||||||
</code>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</q-expansion-item>
|
|
||||||
<q-expansion-item
|
|
||||||
group="api"
|
|
||||||
dense
|
|
||||||
expand-separator
|
|
||||||
label="Update a satsdice link"
|
|
||||||
>
|
|
||||||
<q-card>
|
|
||||||
<q-card-section>
|
|
||||||
<code
|
|
||||||
><span class="text-green">PUT</span>
|
|
||||||
/satsdice/api/v1/links/<satsdice_id></code
|
|
||||||
>
|
|
||||||
<h5 class="text-caption q-mt-sm q-mb-none">Headers</h5>
|
|
||||||
<code>{"X-Api-Key": <admin_key>}</code><br />
|
|
||||||
<h5 class="text-caption q-mt-sm q-mb-none">Body (application/json)</h5>
|
|
||||||
<code
|
|
||||||
>{"title": <string>, "min_satsdiceable": <integer>,
|
|
||||||
"max_satsdiceable": <integer>, "uses": <integer>,
|
|
||||||
"wait_time": <integer>, "is_unique": <boolean>}</code
|
|
||||||
>
|
|
||||||
<h5 class="text-caption q-mt-sm q-mb-none">
|
|
||||||
Returns 200 OK (application/json)
|
|
||||||
</h5>
|
|
||||||
<code>{"lnurl": <string>}</code>
|
|
||||||
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
|
|
||||||
<code
|
|
||||||
>curl -X PUT {{ request.base_url
|
|
||||||
}}satsdice/api/v1/links/<satsdice_id> -d '{"title":
|
|
||||||
<string>, "min_satsdiceable": <integer>,
|
|
||||||
"max_satsdiceable": <integer>, "uses": <integer>,
|
|
||||||
"wait_time": <integer>, "is_unique": <boolean>}' -H
|
|
||||||
"Content-type: application/json" -H "X-Api-Key: {{
|
|
||||||
user.wallets[0].adminkey }}"
|
|
||||||
</code>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</q-expansion-item>
|
|
||||||
<q-expansion-item
|
|
||||||
group="api"
|
|
||||||
dense
|
|
||||||
expand-separator
|
|
||||||
label="Delete a satsdice link"
|
|
||||||
>
|
|
||||||
<q-card>
|
|
||||||
<q-card-section>
|
|
||||||
<code
|
|
||||||
><span class="text-pink">DELETE</span>
|
|
||||||
/satsdice/api/v1/links/<satsdice_id></code
|
|
||||||
>
|
|
||||||
<h5 class="text-caption q-mt-sm q-mb-none">Headers</h5>
|
|
||||||
<code>{"X-Api-Key": <admin_key>}</code><br />
|
|
||||||
<h5 class="text-caption q-mt-sm q-mb-none">Returns 204 NO CONTENT</h5>
|
|
||||||
<code></code>
|
|
||||||
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
|
|
||||||
<code
|
|
||||||
>curl -X DELETE {{ request.base_url
|
|
||||||
}}satsdice/api/v1/links/<satsdice_id> -H "X-Api-Key: {{
|
|
||||||
user.wallets[0].adminkey }}"
|
|
||||||
</code>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</q-expansion-item>
|
|
||||||
<q-expansion-item
|
|
||||||
group="api"
|
|
||||||
dense
|
|
||||||
expand-separator
|
|
||||||
label="Get hash check (for captchas to prevent milking)"
|
|
||||||
>
|
|
||||||
<q-card>
|
|
||||||
<q-card-section>
|
|
||||||
<code
|
|
||||||
><span class="text-blue">GET</span>
|
|
||||||
/satsdice/api/v1/links/<the_hash>/<lnurl_id></code
|
|
||||||
>
|
|
||||||
<h5 class="text-caption q-mt-sm q-mb-none">Headers</h5>
|
|
||||||
<code>{"X-Api-Key": <invoice_key>}</code><br />
|
|
||||||
<h5 class="text-caption q-mt-sm q-mb-none">Body (application/json)</h5>
|
|
||||||
<h5 class="text-caption q-mt-sm q-mb-none">
|
|
||||||
Returns 201 CREATED (application/json)
|
|
||||||
</h5>
|
|
||||||
<code>{"status": <bool>}</code>
|
|
||||||
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
|
|
||||||
<code
|
|
||||||
>curl -X GET {{ request.base_url
|
|
||||||
}}satsdice/api/v1/links/<the_hash>/<lnurl_id> -H
|
|
||||||
"X-Api-Key: {{ user.wallets[0].inkey }}"
|
|
||||||
</code>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</q-expansion-item>
|
|
||||||
<q-expansion-item
|
|
||||||
group="api"
|
|
||||||
dense
|
|
||||||
expand-separator
|
|
||||||
label="Get image to embed"
|
|
||||||
class="q-pb-md"
|
|
||||||
>
|
|
||||||
<q-card>
|
|
||||||
<q-card-section>
|
|
||||||
<code
|
|
||||||
><span class="text-blue">GET</span>
|
|
||||||
/satsdice/img/<lnurl_id></code
|
|
||||||
>
|
|
||||||
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
|
|
||||||
<code
|
|
||||||
>curl -X GET {{ request.base_url }}satsdice/img/<lnurl_id>"
|
|
||||||
</code>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</q-expansion-item>
|
|
||||||
</q-expansion-item>
|
|
|
@ -1,32 +0,0 @@
|
||||||
<q-expansion-item group="extras" icon="info" label="Powered by LNURL">
|
|
||||||
<q-card>
|
|
||||||
<q-card-section>
|
|
||||||
<p>
|
|
||||||
<b>WARNING: LNURL must be used over https or TOR</b><br />
|
|
||||||
LNURL is a range of lightning-network standards that allow us to use
|
|
||||||
lightning-network differently. An LNURL satsdice is the permission for
|
|
||||||
someone to pull a certain amount of funds from a lightning wallet. In
|
|
||||||
this extension time is also added - an amount can be satsdice over a
|
|
||||||
period of time. A typical use case for an LNURL satsdice is a faucet,
|
|
||||||
although it is a very powerful technology, with much further reaching
|
|
||||||
implications. For example, an LNURL satsdice could be minted to pay for
|
|
||||||
a subscription service.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Exploring LNURL and finding use cases, is really helping inform
|
|
||||||
lightning protocol development, rather than the protocol dictating how
|
|
||||||
lightning-network should be engaged with.
|
|
||||||
</p>
|
|
||||||
<small
|
|
||||||
>Check
|
|
||||||
<a
|
|
||||||
class="text-secondary"
|
|
||||||
href="https://github.com/fiatjaf/awesome-lnurl"
|
|
||||||
target="_blank"
|
|
||||||
>Awesome LNURL</a
|
|
||||||
>
|
|
||||||
for further information.</small
|
|
||||||
>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</q-expansion-item>
|
|
|
@ -1,63 +0,0 @@
|
||||||
{% extends "public.html" %} {% block page %}
|
|
||||||
<div class="row q-col-gutter-md justify-center">
|
|
||||||
<div class="col-12 col-sm-6 col-md-5 col-lg-4">
|
|
||||||
<q-card class="q-pa-lg">
|
|
||||||
<q-card-section class="q-pa-none">
|
|
||||||
<div class="text-center">
|
|
||||||
<a class="text-secondary" href="lightning:{{ lnurl }}">
|
|
||||||
<q-responsive :ratio="1" class="q-mx-md">
|
|
||||||
<qrcode
|
|
||||||
:value="'lightning:{{ lnurl }}'"
|
|
||||||
:options="{width: 800}"
|
|
||||||
class="rounded-borders"
|
|
||||||
></qrcode>
|
|
||||||
</q-responsive>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="row q-mt-lg">
|
|
||||||
<q-btn outline color="grey" @click="copyText('{{ lnurl }}')"
|
|
||||||
>Copy Satsdice LNURL</q-btn
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-sm-6 col-md-5 col-lg-4 q-gutter-y-md">
|
|
||||||
<q-card>
|
|
||||||
<q-card-section>
|
|
||||||
<h6 class="text-subtitle1 q-mb-sm q-mt-none">
|
|
||||||
Chance of winning: {% raw %}{{ chance }}{% endraw %}, Amount
|
|
||||||
multiplier: {{ multiplier }}
|
|
||||||
</h6>
|
|
||||||
<p class="q-my-none">
|
|
||||||
Use a LNURL compatible bitcoin wallet to play the satsdice.
|
|
||||||
</p>
|
|
||||||
</q-card-section>
|
|
||||||
<q-card-section class="q-pa-none">
|
|
||||||
<q-separator></q-separator>
|
|
||||||
<q-list> {% include "satsdice/_lnurl.html" %} </q-list>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %} {% block scripts %}
|
|
||||||
<script>
|
|
||||||
Vue.component(VueQrcode.name, VueQrcode)
|
|
||||||
|
|
||||||
new Vue({
|
|
||||||
el: '#vue',
|
|
||||||
mixins: [windowMixin],
|
|
||||||
data: function () {
|
|
||||||
return {
|
|
||||||
here: location.protocol + '//' + location.host,
|
|
||||||
chance: parseFloat('{{chance}}') + '%'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
filters: {
|
|
||||||
percent(val) {
|
|
||||||
return (chance / 100) * 100 + '%'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
|
@ -1,56 +0,0 @@
|
||||||
{% extends "public.html" %} {% block page %}
|
|
||||||
<div class="row q-col-gutter-md justify-center">
|
|
||||||
<div class="col-12 col-sm-6 col-md-5 col-lg-4">
|
|
||||||
<q-card class="q-pa-lg">
|
|
||||||
<q-card-section class="q-pa-none">
|
|
||||||
<div class="text-center">
|
|
||||||
<a class="text-secondary" href="lightning:{{ lnurl }}">
|
|
||||||
<q-responsive :ratio="1" class="q-mx-md">
|
|
||||||
<qrcode
|
|
||||||
:value="'lightning:{{ lnurl }}'"
|
|
||||||
:options="{width: 800}"
|
|
||||||
class="rounded-borders"
|
|
||||||
></qrcode>
|
|
||||||
</q-responsive>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="row q-mt-lg">
|
|
||||||
<q-btn outline color="grey" @click="copyText('{{ lnurl }}')"
|
|
||||||
>Copy winnings LNURL</q-btn
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-sm-6 col-md-5 col-lg-4 q-gutter-y-md">
|
|
||||||
<q-card>
|
|
||||||
<q-card-section>
|
|
||||||
<h6 class="text-subtitle1 q-mb-sm q-mt-none">
|
|
||||||
Congrats! You have won {{ value }}sats (you must claim the sats now)
|
|
||||||
</h6>
|
|
||||||
<p class="q-my-none">
|
|
||||||
Use a LNURL compatible bitcoin wallet to play the satsdice.
|
|
||||||
</p>
|
|
||||||
</q-card-section>
|
|
||||||
<q-card-section class="q-pa-none">
|
|
||||||
<q-separator></q-separator>
|
|
||||||
<q-list> {% include "satsdice/_lnurl.html" %} </q-list>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %} {% block scripts %}
|
|
||||||
<script>
|
|
||||||
Vue.component(VueQrcode.name, VueQrcode)
|
|
||||||
|
|
||||||
new Vue({
|
|
||||||
el: '#vue',
|
|
||||||
mixins: [windowMixin],
|
|
||||||
data: function () {
|
|
||||||
return {
|
|
||||||
here: location.protocol + '//' + location.host
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
|
@ -1,54 +0,0 @@
|
||||||
{% extends "public.html" %} {% from "macros.jinja" import window_vars with
|
|
||||||
context %}{% block page %}
|
|
||||||
<div class="row q-col-gutter-md justify-center">
|
|
||||||
<div class="col-12 col-md-7 col-lg-6 q-gutter-y-md">
|
|
||||||
<q-card class="q-pa-lg">
|
|
||||||
<q-card-section class="q-pa-none">
|
|
||||||
<center>
|
|
||||||
{% if lost %}
|
|
||||||
<h5 class="q-my-none">
|
|
||||||
You lost.
|
|
||||||
<a class="text-secondary" href="/satsdice/{{ link }}"
|
|
||||||
>Play again?</a
|
|
||||||
>
|
|
||||||
</h5>
|
|
||||||
{% endif %} {% if paid %}
|
|
||||||
<h5 class="q-my-none">
|
|
||||||
Winnings spent.
|
|
||||||
<a class="text-secondary" href="/satsdice/{{ link }}"
|
|
||||||
>Play again?</a
|
|
||||||
>
|
|
||||||
</h5>
|
|
||||||
{% endif %}
|
|
||||||
<br />
|
|
||||||
<q-icon
|
|
||||||
name="sentiment_dissatisfied"
|
|
||||||
class="text-grey"
|
|
||||||
style="font-size: 20rem"
|
|
||||||
></q-icon>
|
|
||||||
<br />
|
|
||||||
</center>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %} {% block scripts %}{{ window_vars(user) }}
|
|
||||||
<script>
|
|
||||||
new Vue({
|
|
||||||
el: '#vue',
|
|
||||||
mixins: [windowMixin],
|
|
||||||
data: function () {
|
|
||||||
return {
|
|
||||||
satsdice_lost: '{{lost}}',
|
|
||||||
satsdice_paid: '{{paid}}'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
getUrl() {}
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
console.log('play_location')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
|
@ -1,534 +0,0 @@
|
||||||
{% extends "base.html" %} {% from "macros.jinja" import window_vars with context
|
|
||||||
%} {% block page %}
|
|
||||||
<div class="row q-col-gutter-md">
|
|
||||||
<div class="col-12 col-md-7 q-gutter-y-md">
|
|
||||||
<q-card>
|
|
||||||
<q-card-section>
|
|
||||||
<q-btn unelevated color="primary" @click="formDialog.show = true"
|
|
||||||
>New satsdice</q-btn
|
|
||||||
>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
|
|
||||||
<q-card>
|
|
||||||
<q-card-section>
|
|
||||||
<div class="row items-center no-wrap q-mb-md">
|
|
||||||
<div class="col">
|
|
||||||
<h5 class="text-subtitle1 q-my-none">satsdices</h5>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<q-table
|
|
||||||
dense
|
|
||||||
flat
|
|
||||||
:data="payLinks"
|
|
||||||
row-key="id"
|
|
||||||
:pagination.sync="payLinksTable.pagination"
|
|
||||||
>
|
|
||||||
{% raw %}
|
|
||||||
<template v-slot:header="props">
|
|
||||||
<q-tr :props="props">
|
|
||||||
<q-th style="width: 10%"></q-th>
|
|
||||||
<q-th auto-width style="text-align: left">Title</q-th>
|
|
||||||
<q-th auto-width style="text-align: left">Min bet</q-th>
|
|
||||||
<q-th auto-width style="text-align: left">Max bet</q-th>
|
|
||||||
<q-th auto-width style="text-align: left">Multiplier</q-th>
|
|
||||||
<q-th auto-width style="text-align: left">Haircut</q-th>
|
|
||||||
<q-th auto-width style="text-align: left">Chance</q-th>
|
|
||||||
<q-th auto-width style="text-align: left"></q-th>
|
|
||||||
</q-tr>
|
|
||||||
</template>
|
|
||||||
<template v-slot:body="props">
|
|
||||||
<q-tr :props="props">
|
|
||||||
<q-td style="width: 10%">
|
|
||||||
<q-btn
|
|
||||||
unelevated
|
|
||||||
dense
|
|
||||||
size="xs"
|
|
||||||
icon="visibility"
|
|
||||||
:color="($q.dark.isActive) ? 'grey-7' : 'grey-5'"
|
|
||||||
@click="openQrCodeDialog(props.row.id)"
|
|
||||||
></q-btn>
|
|
||||||
</q-td>
|
|
||||||
|
|
||||||
<q-td auto-width>{{ props.row.title }}</q-td>
|
|
||||||
<q-td auto-width>{{ props.row.min_bet }}</q-td>
|
|
||||||
<q-td auto-width>{{ props.row.max_bet }}</q-td>
|
|
||||||
<q-td auto-width>*{{ props.row.multiplier }}</q-td>
|
|
||||||
<q-td auto-width>{{ props.row.haircut }}</q-td>
|
|
||||||
<q-td auto-width>{{ props.row.chance }}%</q-td>
|
|
||||||
<q-td auto-width>
|
|
||||||
<q-btn
|
|
||||||
flat
|
|
||||||
dense
|
|
||||||
size="xs"
|
|
||||||
@click="openUpdateDialog(props.row.id)"
|
|
||||||
icon="edit"
|
|
||||||
color="light-blue"
|
|
||||||
></q-btn>
|
|
||||||
<q-btn
|
|
||||||
flat
|
|
||||||
dense
|
|
||||||
size="xs"
|
|
||||||
@click="deletePayLink(props.row.id)"
|
|
||||||
icon="cancel"
|
|
||||||
color="pink"
|
|
||||||
></q-btn>
|
|
||||||
</q-td>
|
|
||||||
</q-tr>
|
|
||||||
</template>
|
|
||||||
{% endraw %}
|
|
||||||
</q-table>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-12 col-md-5 q-gutter-y-md">
|
|
||||||
<q-card>
|
|
||||||
<q-card-section>
|
|
||||||
<h6 class="text-subtitle1 q-my-none">
|
|
||||||
{{SITE_TITLE}} Sats Dice extension
|
|
||||||
</h6>
|
|
||||||
</q-card-section>
|
|
||||||
<q-card-section class="q-pa-none">
|
|
||||||
<q-separator></q-separator>
|
|
||||||
<q-list>
|
|
||||||
{% include "satsdice/_api_docs.html" %}
|
|
||||||
<q-separator></q-separator>
|
|
||||||
{% include "satsdice/_lnurl.html" %}
|
|
||||||
</q-list>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<q-dialog v-model="formDialog.show" position="top" @hide="closeFormDialog">
|
|
||||||
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
|
||||||
<q-form @submit="sendFormData" class="q-gutter-md">
|
|
||||||
<q-select
|
|
||||||
filled
|
|
||||||
dense
|
|
||||||
emit-value
|
|
||||||
v-model="formDialog.data.wallet"
|
|
||||||
:options="g.user.walletOptions"
|
|
||||||
label="Wallet *"
|
|
||||||
>
|
|
||||||
</q-select>
|
|
||||||
{% raw %}
|
|
||||||
<q-input
|
|
||||||
filled
|
|
||||||
dense
|
|
||||||
v-model.trim="formDialog.data.title"
|
|
||||||
type="text"
|
|
||||||
label="Title *"
|
|
||||||
></q-input>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col">
|
|
||||||
<q-input
|
|
||||||
class="q-pr-xs"
|
|
||||||
filled
|
|
||||||
dense
|
|
||||||
v-model.trim="formDialog.data.min_bet"
|
|
||||||
type="number"
|
|
||||||
label="Min bet size (sats)"
|
|
||||||
></q-input>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<q-input
|
|
||||||
class="q-pl-xs"
|
|
||||||
filled
|
|
||||||
dense
|
|
||||||
v-model.trim="formDialog.data.max_bet"
|
|
||||||
type="number"
|
|
||||||
label="Max bet size (sats)"
|
|
||||||
></q-input>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<q-input
|
|
||||||
filled
|
|
||||||
dense
|
|
||||||
v-model.trim="formDialog.data.haircut"
|
|
||||||
type="number"
|
|
||||||
label="Haircut (chance of winning % to remove)"
|
|
||||||
></q-input>
|
|
||||||
<center>
|
|
||||||
<q-badge color="secondary" class="q-mb-lg">
|
|
||||||
Multipler: x{{ multiValue }}, Chance of winning: {{ chanceValueCalc
|
|
||||||
| percent }}
|
|
||||||
</q-badge>
|
|
||||||
|
|
||||||
<q-slider
|
|
||||||
style="width: 95%"
|
|
||||||
class="q-pt-lg"
|
|
||||||
v-model="multiValue"
|
|
||||||
:min="1.5"
|
|
||||||
:max="20"
|
|
||||||
:step="2"
|
|
||||||
label
|
|
||||||
label-always
|
|
||||||
color="primary"
|
|
||||||
markers
|
|
||||||
snap
|
|
||||||
></q-slider>
|
|
||||||
</center>
|
|
||||||
|
|
||||||
<div class="row q-mt-lg">
|
|
||||||
<q-btn
|
|
||||||
v-if="formDialog.data.id"
|
|
||||||
unelevated
|
|
||||||
color="primary"
|
|
||||||
type="submit"
|
|
||||||
>Update flip link</q-btn
|
|
||||||
>
|
|
||||||
<q-btn
|
|
||||||
v-else
|
|
||||||
unelevated
|
|
||||||
color="primary"
|
|
||||||
:disable="
|
|
||||||
formDialog.data.wallet == null ||
|
|
||||||
formDialog.data.title == null || formDialog.data.min < 10 || formDialog.data.max < formDialog.data.min"
|
|
||||||
type="submit"
|
|
||||||
>Create satsdice</q-btn
|
|
||||||
>
|
|
||||||
<q-btn v-close-popup flat color="grey" class="q-ml-auto"
|
|
||||||
>Cancel</q-btn
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</q-form>
|
|
||||||
</q-card>
|
|
||||||
</q-dialog>
|
|
||||||
|
|
||||||
<q-dialog v-model="qrCodeDialog.show" position="top">
|
|
||||||
<q-card v-if="qrCodeDialog.data" class="q-pa-lg lnbits__dialog-card">
|
|
||||||
<q-responsive :ratio="1" class="q-mx-xl q-mb-md">
|
|
||||||
<qrcode
|
|
||||||
:value="'lightning:' + qrCodeDialog.data.lnurl"
|
|
||||||
:options="{width: 800}"
|
|
||||||
class="rounded-borders"
|
|
||||||
></qrcode>
|
|
||||||
</q-responsive>
|
|
||||||
<p style="word-break: break-all">
|
|
||||||
<strong>ID:</strong> {{ qrCodeDialog.data.id }}<br />
|
|
||||||
<strong>Amount:</strong> {{ qrCodeDialog.data.amount }}<br />
|
|
||||||
<span v-if="qrCodeDialog.data.currency"
|
|
||||||
><strong>{{ qrCodeDialog.data.currency }} price:</strong> {{
|
|
||||||
fiatRates[qrCodeDialog.data.currency] ?
|
|
||||||
fiatRates[qrCodeDialog.data.currency] + ' sat' : 'Loading...' }}<br
|
|
||||||
/></span>
|
|
||||||
<strong>Accepts comments:</strong> {{ qrCodeDialog.data.comments }}<br />
|
|
||||||
<strong>Dispatches webhook to:</strong> {{ qrCodeDialog.data.webhook
|
|
||||||
}}<br />
|
|
||||||
<strong>On success:</strong> {{ qrCodeDialog.data.success }}<br />
|
|
||||||
</p>
|
|
||||||
{% endraw %}
|
|
||||||
<div class="row q-mt-lg q-gutter-sm">
|
|
||||||
<q-btn
|
|
||||||
outline
|
|
||||||
color="grey"
|
|
||||||
@click="copyText(qrCodeDialog.data.lnurl, 'Satsdice copied to clipboard!')"
|
|
||||||
class="q-ml-sm"
|
|
||||||
>Copy Satsdice LNURL</q-btn
|
|
||||||
>
|
|
||||||
<q-btn
|
|
||||||
outline
|
|
||||||
color="grey"
|
|
||||||
icon="link"
|
|
||||||
@click="copyText(qrCodeDialog.data.pay_url, 'Link copied to clipboard!')"
|
|
||||||
><q-tooltip>Copy shareable link</q-tooltip></q-btn
|
|
||||||
>
|
|
||||||
|
|
||||||
<q-btn
|
|
||||||
outline
|
|
||||||
color="grey"
|
|
||||||
icon="launch"
|
|
||||||
type="a"
|
|
||||||
:href="qrCodeDialog.data.pay_url"
|
|
||||||
target="_blank"
|
|
||||||
><q-tooltip>Launch shareable link</q-tooltip></q-btn
|
|
||||||
>
|
|
||||||
<q-btn
|
|
||||||
outline
|
|
||||||
color="grey"
|
|
||||||
icon="print"
|
|
||||||
type="a"
|
|
||||||
:href="qrCodeDialog.data.print_url"
|
|
||||||
target="_blank"
|
|
||||||
><q-tooltip>Print Satsdice</q-tooltip></q-btn
|
|
||||||
>
|
|
||||||
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Close</q-btn>
|
|
||||||
</div>
|
|
||||||
</q-card>
|
|
||||||
</q-dialog>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% endblock %} {% block scripts %} {{ window_vars(user) }}
|
|
||||||
|
|
||||||
<script>
|
|
||||||
/* globals Quasar, Vue, _, VueQrcode, windowMixin, LNbits, LOCALE */
|
|
||||||
Vue.component(VueQrcode.name, VueQrcode)
|
|
||||||
|
|
||||||
var locationPath = [
|
|
||||||
window.location.protocol,
|
|
||||||
'//',
|
|
||||||
window.location.host,
|
|
||||||
window.location.pathname
|
|
||||||
].join('')
|
|
||||||
|
|
||||||
var mapPayLink = obj => {
|
|
||||||
obj._data = _.clone(obj)
|
|
||||||
obj.date = Quasar.utils.date.formatDate(
|
|
||||||
new Date(obj.time * 1000),
|
|
||||||
'YYYY-MM-DD HH:mm'
|
|
||||||
)
|
|
||||||
obj.amount = new Intl.NumberFormat(LOCALE).format(obj.amount)
|
|
||||||
obj.print_url = [locationPath, 'print/', obj.id].join('')
|
|
||||||
obj.pay_url = [locationPath, obj.id].join('')
|
|
||||||
console.log(obj)
|
|
||||||
return obj
|
|
||||||
}
|
|
||||||
|
|
||||||
new Vue({
|
|
||||||
el: '#vue',
|
|
||||||
mixins: [windowMixin],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
chanceValue: 0,
|
|
||||||
multiValue: 1.5,
|
|
||||||
currencies: [],
|
|
||||||
fiatRates: {},
|
|
||||||
checker: null,
|
|
||||||
payLinks: [],
|
|
||||||
payLinksTable: {
|
|
||||||
pagination: {
|
|
||||||
rowsPerPage: 10
|
|
||||||
}
|
|
||||||
},
|
|
||||||
formDialog: {
|
|
||||||
show: false,
|
|
||||||
fixedAmount: true,
|
|
||||||
data: {
|
|
||||||
haircut: 0,
|
|
||||||
min_bet: 10,
|
|
||||||
max_bet: 1000,
|
|
||||||
currency: 'satoshis',
|
|
||||||
comment_chars: 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
qrCodeDialog: {
|
|
||||||
show: false,
|
|
||||||
data: null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
filters: {
|
|
||||||
percent(val) {
|
|
||||||
return val + '%'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
chanceValueCalc() {
|
|
||||||
this.chanceValue = (
|
|
||||||
(1 / this.multiValue) * 100 -
|
|
||||||
this.formDialog.data.haircut -
|
|
||||||
(1 / this.multiValue) * 10
|
|
||||||
).toFixed(2)
|
|
||||||
return this.chanceValue
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
chanceValueTableCalc(multiplier, haircut) {
|
|
||||||
return ((1 / multiplier) * 100 - haircut).toFixed(2)
|
|
||||||
},
|
|
||||||
getPayLinks() {
|
|
||||||
LNbits.api
|
|
||||||
.request(
|
|
||||||
'GET',
|
|
||||||
'/satsdice/api/v1/links?all_wallets=true',
|
|
||||||
this.g.user.wallets[0].inkey
|
|
||||||
)
|
|
||||||
.then(response => {
|
|
||||||
console.log(response.data)
|
|
||||||
this.payLinks = response.data.map(mapPayLink)
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
clearInterval(this.checker)
|
|
||||||
LNbits.utils.notifyApiError(err)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
closeFormDialog() {
|
|
||||||
this.resetFormData()
|
|
||||||
},
|
|
||||||
openQrCodeDialog(linkId) {
|
|
||||||
var link = _.findWhere(this.payLinks, {id: linkId})
|
|
||||||
console.log(link)
|
|
||||||
if (link.currency) this.updateFiatRate(link.currency)
|
|
||||||
|
|
||||||
this.qrCodeDialog.data = {
|
|
||||||
id: link.id,
|
|
||||||
amount:
|
|
||||||
(link.min === link.max ? link.min : `${link.min} - ${link.max}`) +
|
|
||||||
' ' +
|
|
||||||
(link.currency || 'sat'),
|
|
||||||
currency: link.currency,
|
|
||||||
comments: link.comment_chars
|
|
||||||
? `${link.comment_chars} characters`
|
|
||||||
: 'no',
|
|
||||||
webhook: link.webhook_url || 'nowhere',
|
|
||||||
success:
|
|
||||||
link.success_text || link.success_url
|
|
||||||
? 'Display message "' +
|
|
||||||
link.success_text +
|
|
||||||
'"' +
|
|
||||||
(link.success_url ? ' and URL "' + link.success_url + '"' : '')
|
|
||||||
: 'do nothing',
|
|
||||||
lnurl: link.lnurl,
|
|
||||||
pay_url: link.pay_url,
|
|
||||||
print_url: link.print_url
|
|
||||||
}
|
|
||||||
this.qrCodeDialog.show = true
|
|
||||||
},
|
|
||||||
openUpdateDialog(linkId) {
|
|
||||||
const link = _.findWhere(this.payLinks, {id: linkId})
|
|
||||||
if (link.currency) this.updateFiatRate(link.currency)
|
|
||||||
|
|
||||||
this.formDialog.data = _.clone(link._data)
|
|
||||||
this.formDialog.show = true
|
|
||||||
this.formDialog.fixedAmount =
|
|
||||||
this.formDialog.data.min === this.formDialog.data.max
|
|
||||||
},
|
|
||||||
sendFormData() {
|
|
||||||
const wallet = _.findWhere(this.g.user.wallets, {
|
|
||||||
id: this.formDialog.data.wallet
|
|
||||||
})
|
|
||||||
var data = _.omit(this.formDialog.data, 'wallet')
|
|
||||||
data.min_bet = parseInt(data.min_bet)
|
|
||||||
data.max_bet = parseInt(data.max_bet)
|
|
||||||
data.multiplier = parseFloat(this.multiValue)
|
|
||||||
data.haircut = parseFloat(data.haircut)
|
|
||||||
data.chance = parseFloat(this.chanceValue)
|
|
||||||
data.base_url = window.location.origin
|
|
||||||
|
|
||||||
if (data.currency === 'satoshis') data.currency = null
|
|
||||||
if (isNaN(parseInt(data.comment_chars))) data.comment_chars = 0
|
|
||||||
|
|
||||||
if (data.id) {
|
|
||||||
this.updatePayLink(wallet, data)
|
|
||||||
} else {
|
|
||||||
this.createPayLink(wallet, data)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
resetFormData() {
|
|
||||||
this.formDialog = {
|
|
||||||
show: false,
|
|
||||||
fixedAmount: true,
|
|
||||||
data: {
|
|
||||||
haircut: 0,
|
|
||||||
min_bet: 10,
|
|
||||||
max_bet: 1000,
|
|
||||||
currency: 'satoshis',
|
|
||||||
comment_chars: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
updatePayLink(wallet, data) {
|
|
||||||
let values = _.omit(
|
|
||||||
_.pick(
|
|
||||||
data,
|
|
||||||
'chance',
|
|
||||||
'base_url',
|
|
||||||
'multiplier',
|
|
||||||
'haircut',
|
|
||||||
'title',
|
|
||||||
'min_bet',
|
|
||||||
'max_bet',
|
|
||||||
'webhook_url',
|
|
||||||
'success_text',
|
|
||||||
'success_url',
|
|
||||||
'comment_chars',
|
|
||||||
'currency'
|
|
||||||
),
|
|
||||||
(value, key) =>
|
|
||||||
(key === 'webhook_url' ||
|
|
||||||
key === 'success_text' ||
|
|
||||||
key === 'success_url') &&
|
|
||||||
(value === null || value === '')
|
|
||||||
)
|
|
||||||
|
|
||||||
LNbits.api
|
|
||||||
.request(
|
|
||||||
'PUT',
|
|
||||||
'/satsdice/api/v1/links/' + data.id,
|
|
||||||
wallet.adminkey,
|
|
||||||
values
|
|
||||||
)
|
|
||||||
.then(response => {
|
|
||||||
this.payLinks = _.reject(this.payLinks, obj => obj.id === data.id)
|
|
||||||
this.payLinks.push(mapPayLink(response.data))
|
|
||||||
this.formDialog.show = false
|
|
||||||
this.resetFormData()
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
LNbits.utils.notifyApiError(err)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
createPayLink(wallet, data) {
|
|
||||||
LNbits.api
|
|
||||||
.request('POST', '/satsdice/api/v1/links', wallet.adminkey, data)
|
|
||||||
.then(response => {
|
|
||||||
this.payLinks.push(mapPayLink(response.data))
|
|
||||||
this.formDialog.show = false
|
|
||||||
this.resetFormData()
|
|
||||||
this.getPayLinks()
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
LNbits.utils.notifyApiError(err)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
deletePayLink(linkId) {
|
|
||||||
var link = _.findWhere(this.payLinks, {id: linkId})
|
|
||||||
|
|
||||||
LNbits.utils
|
|
||||||
.confirmDialog('Are you sure you want to delete this pay link?')
|
|
||||||
.onOk(() => {
|
|
||||||
LNbits.api
|
|
||||||
.request(
|
|
||||||
'DELETE',
|
|
||||||
'/satsdice/api/v1/links/' + linkId,
|
|
||||||
_.findWhere(this.g.user.wallets, {id: link.wallet}).adminkey
|
|
||||||
)
|
|
||||||
.then(response => {
|
|
||||||
this.payLinks = _.reject(
|
|
||||||
this.payLinks,
|
|
||||||
obj => obj.id === linkId
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
LNbits.utils.notifyApiError(err)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
updateFiatRate(currency) {
|
|
||||||
LNbits.api
|
|
||||||
.request('GET', '/satsdice/api/v1/rate/' + currency, null)
|
|
||||||
.then(response => {
|
|
||||||
let rates = _.clone(this.fiatRates)
|
|
||||||
rates[currency] = response.data.rate
|
|
||||||
this.fiatRates = rates
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
LNbits.utils.notifyApiError(err)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
if (this.g.user.wallets.length) {
|
|
||||||
var getPayLinks = this.getPayLinks
|
|
||||||
getPayLinks()
|
|
||||||
// this.checker = setInterval(() => {
|
|
||||||
// getPayLinks()
|
|
||||||
// }, 20000)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
|
@ -1,147 +0,0 @@
|
||||||
import random
|
|
||||||
from http import HTTPStatus
|
|
||||||
from io import BytesIO
|
|
||||||
|
|
||||||
import pyqrcode
|
|
||||||
from fastapi import Depends, Query, Request
|
|
||||||
from fastapi.templating import Jinja2Templates
|
|
||||||
from starlette.exceptions import HTTPException
|
|
||||||
from starlette.responses import HTMLResponse
|
|
||||||
|
|
||||||
from lnbits.core.models import User
|
|
||||||
from lnbits.core.views.api import api_payment
|
|
||||||
from lnbits.decorators import check_user_exists
|
|
||||||
|
|
||||||
from . import satsdice_ext, satsdice_renderer
|
|
||||||
from .crud import (
|
|
||||||
create_satsdice_withdraw,
|
|
||||||
get_satsdice_pay,
|
|
||||||
get_satsdice_payment,
|
|
||||||
get_satsdice_withdraw,
|
|
||||||
update_satsdice_payment,
|
|
||||||
)
|
|
||||||
from .models import CreateSatsDiceWithdraw
|
|
||||||
|
|
||||||
templates = Jinja2Templates(directory="templates")
|
|
||||||
|
|
||||||
|
|
||||||
@satsdice_ext.get("/", response_class=HTMLResponse)
|
|
||||||
async def index(request: Request, user: User = Depends(check_user_exists)):
|
|
||||||
return satsdice_renderer().TemplateResponse(
|
|
||||||
"satsdice/index.html", {"request": request, "user": user.dict()}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@satsdice_ext.get("/{link_id}", response_class=HTMLResponse)
|
|
||||||
async def display(request: Request, link_id: str = Query(None)):
|
|
||||||
link = await get_satsdice_pay(link_id)
|
|
||||||
if not link:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=HTTPStatus.NOT_FOUND, detail="satsdice link does not exist."
|
|
||||||
)
|
|
||||||
|
|
||||||
return satsdice_renderer().TemplateResponse(
|
|
||||||
"satsdice/display.html",
|
|
||||||
{
|
|
||||||
"request": request,
|
|
||||||
"chance": link.chance,
|
|
||||||
"multiplier": link.multiplier,
|
|
||||||
"lnurl": link.lnurl(request),
|
|
||||||
"unique": True,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@satsdice_ext.get(
|
|
||||||
"/win/{link_id}/{payment_hash}",
|
|
||||||
name="satsdice.displaywin",
|
|
||||||
response_class=HTMLResponse,
|
|
||||||
)
|
|
||||||
async def displaywin(
|
|
||||||
request: Request, link_id: str = Query(None), payment_hash: str = Query(None)
|
|
||||||
):
|
|
||||||
satsdicelink = await get_satsdice_pay(link_id)
|
|
||||||
if not satsdicelink:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=HTTPStatus.NOT_FOUND, detail="satsdice link does not exist."
|
|
||||||
)
|
|
||||||
withdrawLink = await get_satsdice_withdraw(payment_hash)
|
|
||||||
payment = await get_satsdice_payment(payment_hash)
|
|
||||||
if not payment or payment.lost:
|
|
||||||
return satsdice_renderer().TemplateResponse(
|
|
||||||
"satsdice/error.html",
|
|
||||||
{"request": request, "link": satsdicelink.id, "paid": False, "lost": True},
|
|
||||||
)
|
|
||||||
if withdrawLink:
|
|
||||||
return satsdice_renderer().TemplateResponse(
|
|
||||||
"satsdice/displaywin.html",
|
|
||||||
{
|
|
||||||
"request": request,
|
|
||||||
"value": withdrawLink.value,
|
|
||||||
"chance": satsdicelink.chance,
|
|
||||||
"multiplier": satsdicelink.multiplier,
|
|
||||||
"lnurl": withdrawLink.lnurl(request),
|
|
||||||
"paid": False,
|
|
||||||
"lost": False,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
rand = random.randint(0, 100)
|
|
||||||
chance = satsdicelink.chance
|
|
||||||
status = await api_payment(payment_hash)
|
|
||||||
if not rand < chance or not status["paid"]:
|
|
||||||
await update_satsdice_payment(payment_hash, lost=1)
|
|
||||||
return satsdice_renderer().TemplateResponse(
|
|
||||||
"satsdice/error.html",
|
|
||||||
{"request": request, "link": satsdicelink.id, "paid": False, "lost": True},
|
|
||||||
)
|
|
||||||
await update_satsdice_payment(payment_hash, paid=1)
|
|
||||||
paylink = await get_satsdice_payment(payment_hash)
|
|
||||||
if not paylink:
|
|
||||||
return satsdice_renderer().TemplateResponse(
|
|
||||||
"satsdice/error.html",
|
|
||||||
{"request": request, "link": satsdicelink.id, "paid": False, "lost": True},
|
|
||||||
)
|
|
||||||
|
|
||||||
data = CreateSatsDiceWithdraw(
|
|
||||||
satsdice_pay=satsdicelink.id,
|
|
||||||
value=int(paylink.value * satsdicelink.multiplier),
|
|
||||||
payment_hash=payment_hash,
|
|
||||||
used=0,
|
|
||||||
)
|
|
||||||
|
|
||||||
withdrawLink = await create_satsdice_withdraw(data)
|
|
||||||
return satsdice_renderer().TemplateResponse(
|
|
||||||
"satsdice/displaywin.html",
|
|
||||||
{
|
|
||||||
"request": request,
|
|
||||||
"value": withdrawLink.value,
|
|
||||||
"chance": satsdicelink.chance,
|
|
||||||
"multiplier": satsdicelink.multiplier,
|
|
||||||
"lnurl": withdrawLink.lnurl(request),
|
|
||||||
"paid": False,
|
|
||||||
"lost": False,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@satsdice_ext.get("/img/{link_id}", response_class=HTMLResponse)
|
|
||||||
async def img(link_id):
|
|
||||||
link = await get_satsdice_pay(link_id)
|
|
||||||
if not link:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=HTTPStatus.NOT_FOUND, detail="satsdice link does not exist."
|
|
||||||
)
|
|
||||||
|
|
||||||
qr = pyqrcode.create(link.lnurl)
|
|
||||||
stream = BytesIO()
|
|
||||||
qr.svg(stream, scale=3)
|
|
||||||
return (
|
|
||||||
stream.getvalue(),
|
|
||||||
200,
|
|
||||||
{
|
|
||||||
"Content-Type": "image/svg+xml",
|
|
||||||
"Cache-Control": "no-cache, no-store, must-revalidate",
|
|
||||||
"Pragma": "no-cache",
|
|
||||||
"Expires": "0",
|
|
||||||
},
|
|
||||||
)
|
|
|
@ -1,129 +0,0 @@
|
||||||
from http import HTTPStatus
|
|
||||||
|
|
||||||
from fastapi import Depends, Query, Request
|
|
||||||
from lnurl.exceptions import InvalidUrl as LnurlInvalidUrl
|
|
||||||
from starlette.exceptions import HTTPException
|
|
||||||
|
|
||||||
from lnbits.core.crud import get_user
|
|
||||||
from lnbits.decorators import WalletTypeInfo, get_key_type
|
|
||||||
|
|
||||||
from . import satsdice_ext
|
|
||||||
from .crud import (
|
|
||||||
create_satsdice_pay,
|
|
||||||
delete_satsdice_pay,
|
|
||||||
get_satsdice_pay,
|
|
||||||
get_satsdice_pays,
|
|
||||||
get_withdraw_hash_checkw,
|
|
||||||
update_satsdice_pay,
|
|
||||||
)
|
|
||||||
from .models import CreateSatsDiceLink
|
|
||||||
|
|
||||||
################LNURL pay
|
|
||||||
|
|
||||||
|
|
||||||
@satsdice_ext.get("/api/v1/links")
|
|
||||||
async def api_links(
|
|
||||||
request: Request,
|
|
||||||
wallet: WalletTypeInfo = Depends(get_key_type),
|
|
||||||
all_wallets: bool = Query(False),
|
|
||||||
):
|
|
||||||
wallet_ids = [wallet.wallet.id]
|
|
||||||
|
|
||||||
if all_wallets:
|
|
||||||
user = await get_user(wallet.wallet.user)
|
|
||||||
if user:
|
|
||||||
wallet_ids = user.wallet_ids
|
|
||||||
|
|
||||||
try:
|
|
||||||
links = await get_satsdice_pays(wallet_ids)
|
|
||||||
|
|
||||||
return [{**link.dict(), **{"lnurl": link.lnurl(request)}} for link in links]
|
|
||||||
except LnurlInvalidUrl:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=HTTPStatus.UPGRADE_REQUIRED,
|
|
||||||
detail="LNURLs need to be delivered over a publically accessible `https` domain or Tor.",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@satsdice_ext.get("/api/v1/links/{link_id}")
|
|
||||||
async def api_link_retrieve(
|
|
||||||
link_id: str = Query(None), wallet: WalletTypeInfo = Depends(get_key_type)
|
|
||||||
):
|
|
||||||
link = await get_satsdice_pay(link_id)
|
|
||||||
|
|
||||||
if not link:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=HTTPStatus.NOT_FOUND, detail="Pay link does not exist."
|
|
||||||
)
|
|
||||||
|
|
||||||
if link.wallet != wallet.wallet.id:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=HTTPStatus.FORBIDDEN, detail="Not your pay link."
|
|
||||||
)
|
|
||||||
|
|
||||||
return {**link.dict(), **{"lnurl": link.lnurl}}
|
|
||||||
|
|
||||||
|
|
||||||
@satsdice_ext.post("/api/v1/links", status_code=HTTPStatus.CREATED)
|
|
||||||
@satsdice_ext.put("/api/v1/links/{link_id}", status_code=HTTPStatus.OK)
|
|
||||||
async def api_link_create_or_update(
|
|
||||||
data: CreateSatsDiceLink,
|
|
||||||
wallet: WalletTypeInfo = Depends(get_key_type),
|
|
||||||
link_id: str = Query(None),
|
|
||||||
):
|
|
||||||
if data.min_bet > data.max_bet:
|
|
||||||
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail="Bad request")
|
|
||||||
if link_id:
|
|
||||||
link = await get_satsdice_pay(link_id)
|
|
||||||
if not link:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=HTTPStatus.NOT_FOUND, detail="Satsdice does not exist"
|
|
||||||
)
|
|
||||||
|
|
||||||
if link.wallet != wallet.wallet.id:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=HTTPStatus.FORBIDDEN,
|
|
||||||
detail="Come on, seriously, this isn't your satsdice!",
|
|
||||||
)
|
|
||||||
|
|
||||||
data.wallet = wallet.wallet.id
|
|
||||||
link = await update_satsdice_pay(link_id, **data.dict())
|
|
||||||
else:
|
|
||||||
link = await create_satsdice_pay(wallet_id=wallet.wallet.id, data=data)
|
|
||||||
|
|
||||||
return {**link.dict(), **{"lnurl": link.lnurl}}
|
|
||||||
|
|
||||||
|
|
||||||
@satsdice_ext.delete("/api/v1/links/{link_id}")
|
|
||||||
async def api_link_delete(
|
|
||||||
wallet: WalletTypeInfo = Depends(get_key_type),
|
|
||||||
link_id: str = Query(None),
|
|
||||||
):
|
|
||||||
link = await get_satsdice_pay(link_id)
|
|
||||||
if not link:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=HTTPStatus.NOT_FOUND, detail="Pay link does not exist."
|
|
||||||
)
|
|
||||||
|
|
||||||
if link.wallet != wallet.wallet.id:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=HTTPStatus.FORBIDDEN, detail="Not your pay link."
|
|
||||||
)
|
|
||||||
|
|
||||||
await delete_satsdice_pay(link_id)
|
|
||||||
|
|
||||||
return "", HTTPStatus.NO_CONTENT
|
|
||||||
|
|
||||||
|
|
||||||
##########LNURL withdraw
|
|
||||||
|
|
||||||
|
|
||||||
@satsdice_ext.get(
|
|
||||||
"/api/v1/withdraws/{the_hash}/{lnurl_id}", dependencies=[Depends(get_key_type)]
|
|
||||||
)
|
|
||||||
async def api_withdraw_hash_retrieve(
|
|
||||||
lnurl_id: str = Query(None),
|
|
||||||
the_hash: str = Query(None),
|
|
||||||
):
|
|
||||||
hashCheck = await get_withdraw_hash_checkw(the_hash, lnurl_id)
|
|
||||||
return hashCheck
|
|
Loading…
Add table
Reference in a new issue