From 0e1dbfcd2609b7b52f122600b9df62d581c96000 Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 15 Feb 2023 10:44:13 +0000 Subject: [PATCH] again --- lnbits/extensions/satsdice/README.md | 5 - lnbits/extensions/satsdice/__init__.py | 26 - lnbits/extensions/satsdice/config.json | 6 - lnbits/extensions/satsdice/crud.py | 279 --------- lnbits/extensions/satsdice/lnurl.py | 156 ----- lnbits/extensions/satsdice/migrations.py | 73 --- lnbits/extensions/satsdice/models.py | 134 ----- .../satsdice/static/image/satsdice.png | Bin 22157 -> 0 bytes .../templates/satsdice/_api_docs.html | 198 ------- .../satsdice/templates/satsdice/_lnurl.html | 32 -- .../satsdice/templates/satsdice/display.html | 63 --- .../templates/satsdice/displaywin.html | 56 -- .../satsdice/templates/satsdice/error.html | 54 -- .../satsdice/templates/satsdice/index.html | 534 ------------------ lnbits/extensions/satsdice/views.py | 147 ----- lnbits/extensions/satsdice/views_api.py | 129 ----- 16 files changed, 1892 deletions(-) delete mode 100644 lnbits/extensions/satsdice/README.md delete mode 100644 lnbits/extensions/satsdice/__init__.py delete mode 100644 lnbits/extensions/satsdice/config.json delete mode 100644 lnbits/extensions/satsdice/crud.py delete mode 100644 lnbits/extensions/satsdice/lnurl.py delete mode 100644 lnbits/extensions/satsdice/migrations.py delete mode 100644 lnbits/extensions/satsdice/models.py delete mode 100644 lnbits/extensions/satsdice/static/image/satsdice.png delete mode 100644 lnbits/extensions/satsdice/templates/satsdice/_api_docs.html delete mode 100644 lnbits/extensions/satsdice/templates/satsdice/_lnurl.html delete mode 100644 lnbits/extensions/satsdice/templates/satsdice/display.html delete mode 100644 lnbits/extensions/satsdice/templates/satsdice/displaywin.html delete mode 100644 lnbits/extensions/satsdice/templates/satsdice/error.html delete mode 100644 lnbits/extensions/satsdice/templates/satsdice/index.html delete mode 100644 lnbits/extensions/satsdice/views.py delete mode 100644 lnbits/extensions/satsdice/views_api.py diff --git a/lnbits/extensions/satsdice/README.md b/lnbits/extensions/satsdice/README.md deleted file mode 100644 index c2419930..00000000 --- a/lnbits/extensions/satsdice/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# satsdice - -## Create staic LNURL powered satsdices - -Gambling is dangerous, flip responsibly diff --git a/lnbits/extensions/satsdice/__init__.py b/lnbits/extensions/satsdice/__init__.py deleted file mode 100644 index a13653bf..00000000 --- a/lnbits/extensions/satsdice/__init__.py +++ /dev/null @@ -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 diff --git a/lnbits/extensions/satsdice/config.json b/lnbits/extensions/satsdice/config.json deleted file mode 100644 index 3f4355fe..00000000 --- a/lnbits/extensions/satsdice/config.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "Sats Dice", - "short_description": "LNURL Satoshi dice", - "tile": "/satsdice/static/image/satsdice.png", - "contributors": ["arcbtc"] -} diff --git a/lnbits/extensions/satsdice/crud.py b/lnbits/extensions/satsdice/crud.py deleted file mode 100644 index 6aeaf31f..00000000 --- a/lnbits/extensions/satsdice/crud.py +++ /dev/null @@ -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} diff --git a/lnbits/extensions/satsdice/lnurl.py b/lnbits/extensions/satsdice/lnurl.py deleted file mode 100644 index 2bb59016..00000000 --- a/lnbits/extensions/satsdice/lnurl.py +++ /dev/null @@ -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"} diff --git a/lnbits/extensions/satsdice/migrations.py b/lnbits/extensions/satsdice/migrations.py deleted file mode 100644 index 82ab35ba..00000000 --- a/lnbits/extensions/satsdice/migrations.py +++ /dev/null @@ -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 - ); - """ - ) diff --git a/lnbits/extensions/satsdice/models.py b/lnbits/extensions/satsdice/models.py deleted file mode 100644 index 510b7bde..00000000 --- a/lnbits/extensions/satsdice/models.py +++ /dev/null @@ -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) diff --git a/lnbits/extensions/satsdice/static/image/satsdice.png b/lnbits/extensions/satsdice/static/image/satsdice.png deleted file mode 100644 index 8c7ccaf9622237a318826924d6e790782dfd9cab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22157 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 - - - - - GET /satsdice/api/v1/links -
Headers
- {"X-Api-Key": <invoice_key>}
-
Body (application/json)
-
- Returns 200 OK (application/json) -
- [<satsdice_link_object>, ...] -
Curl example
- curl -X GET {{ request.base_url }}satsdice/api/v1/links -H - "X-Api-Key: {{ user.wallets[0].inkey }}" - -
-
-
- - - - GET - /satsdice/api/v1/links/<satsdice_id> -
Headers
- {"X-Api-Key": <invoice_key>}
-
Body (application/json)
-
- Returns 201 CREATED (application/json) -
- {"lnurl": <string>} -
Curl example
- curl -X GET {{ request.base_url - }}satsdice/api/v1/links/<satsdice_id> -H "X-Api-Key: {{ - user.wallets[0].inkey }}" - -
-
-
- - - - POST /satsdice/api/v1/links -
Headers
- {"X-Api-Key": <admin_key>}
-
Body (application/json)
- {"title": <string>, "min_satsdiceable": <integer>, - "max_satsdiceable": <integer>, "uses": <integer>, - "wait_time": <integer>, "is_unique": <boolean>} -
- Returns 201 CREATED (application/json) -
- {"lnurl": <string>} -
Curl example
- 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 }}" - -
-
-
- - - - PUT - /satsdice/api/v1/links/<satsdice_id> -
Headers
- {"X-Api-Key": <admin_key>}
-
Body (application/json)
- {"title": <string>, "min_satsdiceable": <integer>, - "max_satsdiceable": <integer>, "uses": <integer>, - "wait_time": <integer>, "is_unique": <boolean>} -
- Returns 200 OK (application/json) -
- {"lnurl": <string>} -
Curl example
- 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 }}" - -
-
-
- - - - DELETE - /satsdice/api/v1/links/<satsdice_id> -
Headers
- {"X-Api-Key": <admin_key>}
-
Returns 204 NO CONTENT
- -
Curl example
- curl -X DELETE {{ request.base_url - }}satsdice/api/v1/links/<satsdice_id> -H "X-Api-Key: {{ - user.wallets[0].adminkey }}" - -
-
-
- - - - GET - /satsdice/api/v1/links/<the_hash>/<lnurl_id> -
Headers
- {"X-Api-Key": <invoice_key>}
-
Body (application/json)
-
- Returns 201 CREATED (application/json) -
- {"status": <bool>} -
Curl example
- curl -X GET {{ request.base_url - }}satsdice/api/v1/links/<the_hash>/<lnurl_id> -H - "X-Api-Key: {{ user.wallets[0].inkey }}" - -
-
-
- - - - GET - /satsdice/img/<lnurl_id> -
Curl example
- curl -X GET {{ request.base_url }}satsdice/img/<lnurl_id>" - -
-
-
- diff --git a/lnbits/extensions/satsdice/templates/satsdice/_lnurl.html b/lnbits/extensions/satsdice/templates/satsdice/_lnurl.html deleted file mode 100644 index 750fb586..00000000 --- a/lnbits/extensions/satsdice/templates/satsdice/_lnurl.html +++ /dev/null @@ -1,32 +0,0 @@ - - - -

- WARNING: LNURL must be used over https or TOR
- 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. -

-

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

- Check - Awesome LNURL - for further information. -
-
-
diff --git a/lnbits/extensions/satsdice/templates/satsdice/display.html b/lnbits/extensions/satsdice/templates/satsdice/display.html deleted file mode 100644 index 56e7ebe6..00000000 --- a/lnbits/extensions/satsdice/templates/satsdice/display.html +++ /dev/null @@ -1,63 +0,0 @@ -{% extends "public.html" %} {% block page %} -
-
- - - -
- Copy Satsdice LNURL -
-
-
-
-
- - -
- Chance of winning: {% raw %}{{ chance }}{% endraw %}, Amount - multiplier: {{ multiplier }} -
-

- Use a LNURL compatible bitcoin wallet to play the satsdice. -

-
- - - {% include "satsdice/_lnurl.html" %} - -
-
-
-{% endblock %} {% block scripts %} - -{% endblock %} diff --git a/lnbits/extensions/satsdice/templates/satsdice/displaywin.html b/lnbits/extensions/satsdice/templates/satsdice/displaywin.html deleted file mode 100644 index f3ce36d5..00000000 --- a/lnbits/extensions/satsdice/templates/satsdice/displaywin.html +++ /dev/null @@ -1,56 +0,0 @@ -{% extends "public.html" %} {% block page %} -
-
- - - -
- Copy winnings LNURL -
-
-
-
-
- - -
- Congrats! You have won {{ value }}sats (you must claim the sats now) -
-

- Use a LNURL compatible bitcoin wallet to play the satsdice. -

-
- - - {% include "satsdice/_lnurl.html" %} - -
-
-
-{% endblock %} {% block scripts %} - -{% endblock %} diff --git a/lnbits/extensions/satsdice/templates/satsdice/error.html b/lnbits/extensions/satsdice/templates/satsdice/error.html deleted file mode 100644 index 63f726d5..00000000 --- a/lnbits/extensions/satsdice/templates/satsdice/error.html +++ /dev/null @@ -1,54 +0,0 @@ -{% extends "public.html" %} {% from "macros.jinja" import window_vars with -context %}{% block page %} -
-
- - -
- {% if lost %} -
- You lost. - Play again? -
- {% endif %} {% if paid %} -
- Winnings spent. - Play again? -
- {% endif %} -
- -
-
-
-
-
-
-{% endblock %} {% block scripts %}{{ window_vars(user) }} - -{% endblock %} diff --git a/lnbits/extensions/satsdice/templates/satsdice/index.html b/lnbits/extensions/satsdice/templates/satsdice/index.html deleted file mode 100644 index 27744273..00000000 --- a/lnbits/extensions/satsdice/templates/satsdice/index.html +++ /dev/null @@ -1,534 +0,0 @@ -{% extends "base.html" %} {% from "macros.jinja" import window_vars with context -%} {% block page %} -
-
- - - New satsdice - - - - - -
-
-
satsdices
-
-
- - {% raw %} - - - {% endraw %} - -
-
-
- -
- - -
- {{SITE_TITLE}} Sats Dice extension -
-
- - - - {% include "satsdice/_api_docs.html" %} - - {% include "satsdice/_lnurl.html" %} - - -
-
- - - - - - - {% raw %} - - -
-
- -
-
- -
-
- - -
- - Multipler: x{{ multiValue }}, Chance of winning: {{ chanceValueCalc - | percent }} - - - -
- -
- Update flip link - Create satsdice - Cancel -
-
-
-
- - - - - - -

- ID: {{ qrCodeDialog.data.id }}
- Amount: {{ qrCodeDialog.data.amount }}
- {{ qrCodeDialog.data.currency }} price: {{ - fiatRates[qrCodeDialog.data.currency] ? - fiatRates[qrCodeDialog.data.currency] + ' sat' : 'Loading...' }}
- Accepts comments: {{ qrCodeDialog.data.comments }}
- Dispatches webhook to: {{ qrCodeDialog.data.webhook - }}
- On success: {{ qrCodeDialog.data.success }}
-

- {% endraw %} -
- Copy Satsdice LNURL - Copy shareable link - - Launch shareable link - Print Satsdice - Close -
-
-
-
- -{% endblock %} {% block scripts %} {{ window_vars(user) }} - - -{% endblock %} diff --git a/lnbits/extensions/satsdice/views.py b/lnbits/extensions/satsdice/views.py deleted file mode 100644 index e51c1e10..00000000 --- a/lnbits/extensions/satsdice/views.py +++ /dev/null @@ -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", - }, - ) diff --git a/lnbits/extensions/satsdice/views_api.py b/lnbits/extensions/satsdice/views_api.py deleted file mode 100644 index 57ab26b8..00000000 --- a/lnbits/extensions/satsdice/views_api.py +++ /dev/null @@ -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