merge main

This commit is contained in:
dni ⚡ 2023-01-04 13:45:18 +01:00
commit 799451ce49
148 changed files with 443 additions and 406 deletions

View File

@ -11,7 +11,11 @@ LNBITS_ALLOWED_USERS=""
LNBITS_ADMIN_USERS=""
# Extensions only admin can access
LNBITS_ADMIN_EXTENSIONS="ngrok, admin"
# Enable Admin GUI, available for the first user in LNBITS_ADMIN_USERS if available
# Warning: Enabling this will make LNbits ignore this configuration file. Your settings will
# be stored in your database and you will be able to change them only through the Admin UI.
# Disable this to make LNbits use this config file again.
LNBITS_ADMIN_UI=false
LNBITS_DEFAULT_WALLET_NAME="LNbits wallet"

View File

@ -25,7 +25,7 @@ LNbits is a very simple Python server that sits on top of any funding source, an
LNbits can run on top of any lightning-network funding source, currently there is support for LND, c-lightning, Spark, LNpay, OpenNode, lntxbot, with more being added regularly.
See [legend.lnbits.org](https://legend.lnbits.org) for more detailed documentation.
See [docs.lnbits.org](https://docs.lnbits.org) for more detailed documentation.
Checkout the LNbits [YouTube](https://www.youtube.com/playlist?list=PLPj3KCksGbSYG0ciIQUWJru1dWstPHshe) video series.
@ -70,7 +70,7 @@ Wallets can be easily generated and given out to people at events (one click mul
If you like this project and might even use or extend it, why not [send some tip love](https://legend.lnbits.com/paywall/GAqKguK5S8f6w5VNjS9DfK)!
[docs]: https://legend.lnbits.org/
[docs]: https://docs.lnbits.org/
[docs-badge]: https://img.shields.io/badge/docs-lnbits.org-673ab7.svg
[github-mypy]: https://github.com/lnbits/lnbits/actions?query=workflow%3Amypy
[github-mypy-badge]: https://github.com/lnbits/lnbits/workflows/mypy/badge.svg

View File

@ -1 +1 @@
legend.lnbits.org
docs.lnbits.org

View File

@ -3,7 +3,7 @@ remote_theme: pmarsceill/just-the-docs
color_scheme: dark
logo: "/logos/lnbits-full--inverse.png"
search_enabled: true
url: https://legend.lnbits.org
url: https://docs.lnbits.org
aux_links:
"LNbits on GitHub":
- "//github.com/lnbits/lnbits"

View File

@ -9,4 +9,4 @@ nav_order: 3
API reference
=============
[Swagger Docs](https://legend.lnbits.org/devs/swagger.html)
[Swagger Docs](https://docs.lnbits.org/devs/swagger.html)

View File

@ -74,7 +74,7 @@ def decode(pr: str) -> Invoice:
data_length = len(tagdata) / 5
if tag == "d":
invoice.description = _trim_to_bytes(tagdata).decode("utf-8")
invoice.description = _trim_to_bytes(tagdata).decode()
elif tag == "h" and data_length == 52:
invoice.description_hash = _trim_to_bytes(tagdata).hex()
elif tag == "p" and data_length == 52:
@ -260,7 +260,7 @@ class LnAddr(object):
def __str__(self):
return "LnAddr[{}, amount={}{} tags=[{}]]".format(
bytes.hex(self.pubkey.serialize()).decode("utf-8"),
bytes.hex(self.pubkey.serialize()).decode(),
self.amount,
self.currency,
", ".join([k + "=" + str(v) for k, v in self.tags]),

View File

@ -46,8 +46,8 @@ class Wallet(BaseModel):
return ""
def lnurlauth_key(self, domain: str) -> SigningKey:
hashing_key = hashlib.sha256(self.id.encode("utf-8")).digest()
linking_key = hmac.digest(hashing_key, domain.encode("utf-8"), "sha256")
hashing_key = hashlib.sha256(self.id.encode()).digest()
linking_key = hmac.digest(hashing_key, domain.encode(), "sha256")
return SigningKey.from_string(
linking_key, curve=SECP256k1, hashfunc=hashlib.sha256

View File

@ -23,14 +23,55 @@
>
<q-card>
<q-card-section>
<q-icon
:name="extension.icon"
color="grey-5"
style="font-size: 4rem"
></q-icon>
{% raw %}
<h5 class="q-mt-lg q-mb-xs">{{ extension.name }}</h5>
<small>{{ extension.shortDescription }} </small>{% endraw %}
<div class="row">
<div class="col-3">
<q-img
:src="extension.tile"
spinner-color="white"
style="max-width: 100%"
></q-img>
</div>
<div class="col-9 q-pl-sm">
{% raw %}
<div class="text-h5 gt-sm q-mt-sm q-mb-xs">
{{ extension.name }}
</div>
<div
class="text-subtitle2 gt-sm"
style="font-size: 11px; height: 34px"
>
{{ extension.shortDescription }}
</div>
<div class="text-subtitle1 lt-md q-mt-sm q-mb-xs">
{{ extension.name }}
</div>
<div
class="text-subtitle2 lt-md"
style="font-size: 9px; height: 34px"
>
{{ extension.shortDescription }}
</div>
{% endraw %}
</div>
</div>
</q-card-section>
<q-card-section>
<div>
<q-rating
class="gt-sm"
disable
size="2em"
:max="5"
color="primary"
></q-rating
><q-rating
class="lt-md"
size="1.5em"
:max="5"
color="primary"
></q-rating
><q-tooltip>Ratings coming soon</q-tooltip>
</div>
</q-card-section>
<q-separator></q-separator>
<q-card-actions>

View File

@ -23,7 +23,7 @@
<strong>{% raw %}{{ formattedBalance }} {% endraw %}</strong>
{{LNBITS_DENOMINATION}}
<q-btn
v-if="'{{user.admin}}' == 'True'"
v-if="'{{user.super_user}}' == 'True'"
flat
round
color="primary"
@ -36,27 +36,16 @@
v-model="credit"
>
<q-input
v-if="'{{LNBITS_DENOMINATION}}' != 'sats'"
label="Amount to credit account"
filled
label="{{LNBITS_DENOMINATION}} to credit"
hint="Press Enter to credit account"
v-model="scope.value"
dense
autofocus
mask="#.##"
:mask="'{{LNBITS_DENOMINATION}}' != 'sats' ? '#.##' : '#'"
fill-mask="0"
reverse-fill-mask
@keyup.enter="updateBalance(scope.value)"
>
<template v-slot:append>
<q-icon name="edit" />
</template>
</q-input>
<q-input
v-else
type="number"
label="Amount to credit account"
v-model="scope.value"
dense
autofocus
:step="'{{LNBITS_DENOMINATION}}' != 'sats' ? '0.01' : '1'"
@keyup.enter="updateBalance(scope.value)"
>
<template v-slot:append>
@ -479,10 +468,10 @@
dense
v-model.number="receive.data.amount"
:label="'Amount (' + receive.unit + ') *'"
:mask="receive.unit != 'sat' ? '#.##' : '#'"
:mask="receive.unit != 'sats' ? '#.##' : '#'"
fill-mask="0"
reverse-fill-mask
:step="receive.unit != 'sat' ? '0.01' : '1'"
:step="receive.unit != 'sats' ? '0.01' : '1'"
:min="receive.minMax[0]"
:max="receive.minMax[1]"
:readonly="receive.lnurl && receive.lnurl.fixed"

View File

@ -17,7 +17,7 @@ from ..crud import delete_admin_settings, get_admin_settings, update_admin_setti
@core_app.get("/admin/api/v1/settings/")
async def api_get_settings(
user: User = Depends(check_admin), # type: ignore
user: User = Depends(check_admin),
) -> Optional[AdminSettings]:
admin_settings = await get_admin_settings(user.super_user)
return admin_settings

View File

@ -12,6 +12,7 @@ import async_timeout
import httpx
import pyqrcode
from fastapi import (
Body,
Depends,
Header,
Query,
@ -21,7 +22,6 @@ from fastapi import (
WebSocketDisconnect,
)
from fastapi.exceptions import HTTPException
from fastapi.params import Body
from loguru import logger
from pydantic import BaseModel
from pydantic.fields import Field
@ -251,7 +251,7 @@ async def api_payments_pay_invoice(bolt11: str, wallet: Wallet):
)
async def api_payments_create(
wallet: WalletTypeInfo = Depends(require_invoice_key),
invoiceData: CreateInvoiceData = Body(...), # type: ignore
invoiceData: CreateInvoiceData = Body(...),
):
if invoiceData.out is True and wallet.wallet_type == 0:
if not invoiceData.bolt11:
@ -387,7 +387,7 @@ async def subscribe_wallet_invoices(request: Request, wallet: Wallet):
jdata = json.dumps(dict(data.dict(), pending=False))
yield dict(data=jdata, event=typ)
except asyncio.CancelledError as e:
except asyncio.CancelledError:
logger.debug(f"removing listener for wallet {uid}")
api_invoice_listeners.pop(uid)
task.cancel()
@ -536,7 +536,7 @@ async def api_lnurlscan(code: str, wallet: WalletTypeInfo = Depends(get_key_type
params.update(
description_hash=hashlib.sha256(
data["metadata"].encode("utf-8")
data["metadata"].encode()
).hexdigest()
)
metadata = json.loads(data["metadata"])

View File

@ -2,9 +2,8 @@ import asyncio
from http import HTTPStatus
from typing import Optional
from fastapi import Request, status
from fastapi import Depends, Query, Request, status
from fastapi.exceptions import HTTPException
from fastapi.params import Depends, Query
from fastapi.responses import FileResponse, RedirectResponse
from fastapi.routing import APIRouter
from loguru import logger
@ -49,9 +48,9 @@ async def home(request: Request, lightning: str = ""):
)
async def extensions(
request: Request,
user: User = Depends(check_user_exists), # type: ignore
enable: str = Query(None), # type: ignore
disable: str = Query(None), # type: ignore
user: User = Depends(check_user_exists),
enable: str = Query(None),
disable: str = Query(None),
):
extension_to_enable = enable
extension_to_disable = disable
@ -103,10 +102,10 @@ nothing: create everything<br>
""",
)
async def wallet(
request: Request = Query(None), # type: ignore
nme: Optional[str] = Query(None), # type: ignore
usr: Optional[UUID4] = Query(None), # type: ignore
wal: Optional[UUID4] = Query(None), # type: ignore
request: Request = Query(None),
nme: Optional[str] = Query(None),
usr: Optional[UUID4] = Query(None),
wal: Optional[UUID4] = Query(None),
):
user_id = usr.hex if usr else None
wallet_id = wal.hex if wal else None
@ -132,6 +131,8 @@ async def wallet(
)
if user_id == settings.super_user or user_id in settings.lnbits_admin_users:
user.admin = True
if user_id == settings.super_user:
user.super_user = True
if not wallet_id:
if user.wallets and not wallet_name: # type: ignore
@ -217,7 +218,7 @@ async def lnurl_full_withdraw_callback(request: Request):
@core_html_routes.get("/deletewallet", response_class=RedirectResponse)
async def deletewallet(request: Request, wal: str = Query(...), usr: str = Query(...)): # type: ignore
async def deletewallet(wal: str = Query(...), usr: str = Query(...)):
user = await get_user(usr)
user_wallet_ids = [u.id for u in user.wallets] # type: ignore
@ -313,7 +314,7 @@ async def manifest(usr: str):
@core_html_routes.get("/admin", response_class=HTMLResponse)
async def index(request: Request, user: User = Depends(check_admin)): # type: ignore
async def index(request: Request, user: User = Depends(check_admin)):
WALLET = get_wallet_class()
_, balance = await WALLET.status()

View File

@ -1,11 +1,8 @@
from http import HTTPStatus
from typing import Union
from cerberus import Validator # type: ignore
from fastapi import status
from fastapi import Security, status
from fastapi.exceptions import HTTPException
from fastapi.openapi.models import APIKey, APIKeyIn
from fastapi.params import Security
from fastapi.security.api_key import APIKeyHeader, APIKeyQuery
from fastapi.security.base import SecurityBase
from pydantic.types import UUID4
@ -118,8 +115,8 @@ api_key_query = APIKeyQuery(
async def get_key_type(
r: Request,
api_key_header: str = Security(api_key_header), # type: ignore
api_key_query: str = Security(api_key_query), # type: ignore
api_key_header: str = Security(api_key_header),
api_key_query: str = Security(api_key_query),
) -> WalletTypeInfo:
# 0: admin
# 1: invoice
@ -174,8 +171,8 @@ async def get_key_type(
async def require_admin_key(
r: Request,
api_key_header: str = Security(api_key_header), # type: ignore
api_key_query: str = Security(api_key_query), # type: ignore
api_key_header: str = Security(api_key_header),
api_key_query: str = Security(api_key_query),
):
token = api_key_header or api_key_query
@ -200,8 +197,8 @@ async def require_admin_key(
async def require_invoice_key(
r: Request,
api_key_header: str = Security(api_key_header), # type: ignore
api_key_query: str = Security(api_key_query), # type: ignore
api_key_header: str = Security(api_key_header),
api_key_query: str = Security(api_key_query),
):
token = api_key_header or api_key_query

View File

@ -1,6 +1,6 @@
{
"name": "Bleskomat",
"short_description": "Connect a Bleskomat ATM to an lnbits",
"icon": "money",
"tile": "/bleskomat/static/image/bleskomat.png",
"contributors": ["chill117"]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -1,6 +1,6 @@
{
"name": "Bolt Cards",
"short_description": "Self custody Bolt Cards with one time LNURLw",
"icon": "payment",
"tile": "/boltcards/static/image/boltcard.png",
"contributors": ["iwarpbtc", "arcbtc", "leesalminen"]
}

View File

@ -213,7 +213,7 @@ async def lnurlp_callback(
memo=f"Refund {hit_id}",
unhashed_description=LnurlPayMetadata(
json.dumps([["text/plain", "Refund"]])
).encode("utf-8"),
).encode(),
extra={"refund": hit_id},
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@ -1,6 +1,7 @@
import asyncio
from fastapi import APIRouter
from fastapi.staticfiles import StaticFiles
from lnbits.db import Database
from lnbits.helpers import template_renderer
@ -15,6 +16,14 @@ def boltz_renderer():
return template_renderer(["lnbits/extensions/boltz/templates"])
boltz_static_files = [
{
"path": "/boltz/static",
"app": StaticFiles(directory="lnbits/extensions/boltz/static"),
"name": "boltz_static",
}
]
from .tasks import check_for_pending_swaps, wait_for_paid_invoices
from .views import * # noqa
from .views_api import * # noqa

View File

@ -55,7 +55,7 @@ async def create_swap(data: CreateSubmarineSwap) -> SubmarineSwap:
raise
refund_privkey = ec.PrivateKey(os.urandom(32), True, net)
refund_pubkey_hex = bytes.hex(refund_privkey.sec()).decode("UTF-8")
refund_pubkey_hex = bytes.hex(refund_privkey.sec()).decode()
res = req_wrap(
"post",
@ -120,7 +120,7 @@ async def create_reverse_swap(
return False
claim_privkey = ec.PrivateKey(os.urandom(32), True, net)
claim_pubkey_hex = bytes.hex(claim_privkey.sec()).decode("UTF-8")
claim_pubkey_hex = bytes.hex(claim_privkey.sec()).decode()
preimage = os.urandom(32)
preimage_hash = sha256(preimage).hexdigest()

View File

@ -1,6 +1,6 @@
{
"name": "Boltz",
"short_description": "Perform onchain/offchain swaps",
"icon": "swap_horiz",
"tile": "/boltz/static/image/boltz.png",
"contributors": ["dni"]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View File

@ -1,7 +1,7 @@
{
"name": "Cashu",
"short_description": "Ecash mint and wallet",
"icon": "account_balance",
"tile": "/cashu/static/image/cashu.png",
"contributors": ["calle", "vlad", "arcbtc"],
"hidden": false
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -1,7 +1,6 @@
from http import HTTPStatus
from fastapi import Request
from fastapi.params import Depends
from fastapi import Depends, Request
from fastapi.templating import Jinja2Templates
from starlette.exceptions import HTTPException
from starlette.responses import HTMLResponse
@ -18,7 +17,7 @@ templates = Jinja2Templates(directory="templates")
@cashu_ext.get("/", response_class=HTMLResponse)
async def index(
request: Request,
user: User = Depends(check_user_exists), # type: ignore
user: User = Depends(check_user_exists),
):
return cashu_renderer().TemplateResponse(
"cashu/index.html", {"request": request, "user": user.dict()}

View File

@ -1,10 +1,7 @@
import json
import math
from http import HTTPStatus
from typing import Dict, List, Union
import httpx
# -------- cashu imports
from cashu.core.base import (
BlindedSignature,
@ -17,14 +14,10 @@ from cashu.core.base import (
MeltRequest,
MintRequest,
PostSplitResponse,
Proof,
SplitRequest,
)
from fastapi import Query
from fastapi.params import Depends
from lnurl import decode as decode_lnurl
from fastapi import Depends, Query
from loguru import logger
from secp256k1 import PublicKey
from starlette.exceptions import HTTPException
from lnbits import bolt11
@ -35,7 +28,6 @@ from lnbits.core.services import (
fee_reserve,
pay_invoice,
)
from lnbits.core.views.api import api_payment
from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
from lnbits.helpers import urlsafe_short_hash
from lnbits.wallets.base import PaymentStatus
@ -63,7 +55,7 @@ if not LIGHTNING:
@cashu_ext.get("/api/v1/mints", status_code=HTTPStatus.OK)
async def api_cashus(
all_wallets: bool = Query(False), wallet: WalletTypeInfo = Depends(get_key_type) # type: ignore
all_wallets: bool = Query(False), wallet: WalletTypeInfo = Depends(get_key_type)
):
"""
Get all mints of this wallet.
@ -80,7 +72,7 @@ async def api_cashus(
@cashu_ext.post("/api/v1/mints", status_code=HTTPStatus.CREATED)
async def api_cashu_create(
data: Cashu,
wallet: WalletTypeInfo = Depends(get_key_type), # type: ignore
wallet: WalletTypeInfo = Depends(get_key_type),
):
"""
Create a new mint for this wallet.
@ -98,7 +90,7 @@ async def api_cashu_create(
@cashu_ext.delete("/api/v1/mints/{cashu_id}")
async def api_cashu_delete(
cashu_id: str, wallet: WalletTypeInfo = Depends(require_admin_key) # type: ignore
cashu_id: str, wallet: WalletTypeInfo = Depends(require_admin_key)
):
"""
Delete an existing cashu mint.

View File

@ -1,7 +1,7 @@
{
"name": "Streamer Copilot",
"short_description": "Video tips/animations/webhooks",
"icon": "face",
"tile": "/copilot/static/bitcoin-streaming.png",
"contributors": [
"arcbtc"
]

View File

@ -75,7 +75,7 @@ async def lnurl_callback(
memo=cp.lnurl_title,
unhashed_description=(
LnurlPayMetadata(json.dumps([["text/plain", str(cp.lnurl_title)]]))
).encode("utf-8"),
).encode(),
extra={"tag": "copilot", "copilotid": cp.id, "comment": comment},
)
payResponse = {"pr": payment_request, "routes": []}

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -1,7 +1,6 @@
from typing import List
from fastapi import Request, WebSocket, WebSocketDisconnect
from fastapi.params import Depends
from fastapi import Depends, Request
from fastapi.templating import Jinja2Templates
from starlette.responses import HTMLResponse # type: ignore
@ -9,15 +8,12 @@ from lnbits.core.models import User
from lnbits.decorators import check_user_exists
from . import copilot_ext, copilot_renderer
from .crud import get_copilot
templates = Jinja2Templates(directory="templates")
@copilot_ext.get("/", response_class=HTMLResponse)
async def index(
request: Request, user: User = Depends(check_user_exists) # type: ignore
):
async def index(request: Request, user: User = Depends(check_user_exists)):
return copilot_renderer().TemplateResponse(
"copilot/index.html", {"request": request, "user": user.dict()}
)

View File

@ -1,8 +1,6 @@
from http import HTTPStatus
from fastapi import Request
from fastapi.param_functions import Query
from fastapi.params import Depends
from fastapi import Depends, Query, Request
from starlette.exceptions import HTTPException
from lnbits.core.services import websocketUpdater
@ -22,9 +20,7 @@ from .models import CreateCopilotData
@copilot_ext.get("/api/v1/copilot")
async def api_copilots_retrieve(
req: Request, wallet: WalletTypeInfo = Depends(get_key_type) # type: ignore
):
async def api_copilots_retrieve(wallet: WalletTypeInfo = Depends(get_key_type)):
wallet_user = wallet.wallet.user
copilots = [copilot.dict() for copilot in await get_copilots(wallet_user)]
try:
@ -37,7 +33,7 @@ async def api_copilots_retrieve(
async def api_copilot_retrieve(
req: Request,
copilot_id: str = Query(None),
wallet: WalletTypeInfo = Depends(get_key_type), # type: ignore
wallet: WalletTypeInfo = Depends(get_key_type),
):
copilot = await get_copilot(copilot_id)
if not copilot:
@ -54,7 +50,7 @@ async def api_copilot_retrieve(
async def api_copilot_create_or_update(
data: CreateCopilotData,
copilot_id: str = Query(None),
wallet: WalletTypeInfo = Depends(require_admin_key), # type: ignore
wallet: WalletTypeInfo = Depends(require_admin_key),
):
data.user = wallet.wallet.user
data.wallet = wallet.wallet.id
@ -68,7 +64,7 @@ async def api_copilot_create_or_update(
@copilot_ext.delete("/api/v1/copilot/{copilot_id}")
async def api_copilot_delete(
copilot_id: str = Query(None),
wallet: WalletTypeInfo = Depends(require_admin_key), # type: ignore
wallet: WalletTypeInfo = Depends(require_admin_key),
):
copilot = await get_copilot(copilot_id)

View File

@ -1,6 +1,6 @@
{
"name": "Discord Bot",
"short_description": "Generate users and wallets",
"icon": "person_add",
"tile": "/discordbot/static/image/discordbot.png",
"contributors": ["bitcoingamer21"]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -1,5 +1,4 @@
from fastapi import Request
from fastapi.params import Depends
from fastapi import Depends, Request
from starlette.responses import HTMLResponse
from lnbits.core.models import User
@ -9,9 +8,7 @@ from . import discordbot_ext, discordbot_renderer
@discordbot_ext.get("/", response_class=HTMLResponse)
async def index(
request: Request, user: User = Depends(check_user_exists) # type: ignore
):
async def index(request: Request, user: User = Depends(check_user_exists)):
return discordbot_renderer().TemplateResponse(
"discordbot/index.html", {"request": request, "user": user.dict()}
)

View File

@ -1,7 +1,6 @@
from http import HTTPStatus
from fastapi import Query
from fastapi.params import Depends
from fastapi import Depends, Query
from starlette.exceptions import HTTPException
from lnbits.core import update_user_extension
@ -28,16 +27,14 @@ from .models import CreateUserData, CreateUserWallet
@discordbot_ext.get("/api/v1/users", status_code=HTTPStatus.OK)
async def api_discordbot_users(
wallet: WalletTypeInfo = Depends(get_key_type), # type: ignore
wallet: WalletTypeInfo = Depends(get_key_type),
):
user_id = wallet.wallet.user
return [user.dict() for user in await get_discordbot_users(user_id)]
@discordbot_ext.get("/api/v1/users/{user_id}", status_code=HTTPStatus.OK)
async def api_discordbot_user(
user_id, wallet: WalletTypeInfo = Depends(get_key_type) # type: ignore
):
async def api_discordbot_user(user_id, wallet: WalletTypeInfo = Depends(get_key_type)):
user = await get_discordbot_user(user_id)
if user:
return user.dict()
@ -45,7 +42,7 @@ async def api_discordbot_user(
@discordbot_ext.post("/api/v1/users", status_code=HTTPStatus.CREATED)
async def api_discordbot_users_create(
data: CreateUserData, wallet: WalletTypeInfo = Depends(get_key_type) # type: ignore
data: CreateUserData, wallet: WalletTypeInfo = Depends(get_key_type)
):
user = await create_discordbot_user(data)
full = user.dict()
@ -57,7 +54,7 @@ async def api_discordbot_users_create(
@discordbot_ext.delete("/api/v1/users/{user_id}")
async def api_discordbot_users_delete(
user_id, wallet: WalletTypeInfo = Depends(get_key_type) # type: ignore
user_id, wallet: WalletTypeInfo = Depends(get_key_type)
):
user = await get_discordbot_user(user_id)
if not user:
@ -89,7 +86,7 @@ async def api_discordbot_activate_extension(
@discordbot_ext.post("/api/v1/wallets")
async def api_discordbot_wallets_create(
data: CreateUserWallet, wallet: WalletTypeInfo = Depends(get_key_type) # type: ignore
data: CreateUserWallet, wallet: WalletTypeInfo = Depends(get_key_type)
):
user = await create_discordbot_wallet(
user_id=data.user_id, wallet_name=data.wallet_name, admin_id=data.admin_id
@ -99,7 +96,7 @@ async def api_discordbot_wallets_create(
@discordbot_ext.get("/api/v1/wallets")
async def api_discordbot_wallets(
wallet: WalletTypeInfo = Depends(get_key_type), # type: ignore
wallet: WalletTypeInfo = Depends(get_key_type),
):
admin_id = wallet.wallet.user
return await get_discordbot_wallets(admin_id)
@ -107,21 +104,21 @@ async def api_discordbot_wallets(
@discordbot_ext.get("/api/v1/transactions/{wallet_id}")
async def api_discordbot_wallet_transactions(
wallet_id, wallet: WalletTypeInfo = Depends(get_key_type) # type: ignore
wallet_id, wallet: WalletTypeInfo = Depends(get_key_type)
):
return await get_discordbot_wallet_transactions(wallet_id)
@discordbot_ext.get("/api/v1/wallets/{user_id}")
async def api_discordbot_users_wallets(
user_id, wallet: WalletTypeInfo = Depends(get_key_type) # type: ignore
user_id, wallet: WalletTypeInfo = Depends(get_key_type)
):
return await get_discordbot_users_wallets(user_id)
@discordbot_ext.delete("/api/v1/wallets/{wallet_id}")
async def api_discordbot_wallets_delete(
wallet_id, wallet: WalletTypeInfo = Depends(get_key_type) # type: ignore
wallet_id, wallet: WalletTypeInfo = Depends(get_key_type)
):
get_wallet = await get_discordbot_wallet(wallet_id)
if not get_wallet:

View File

@ -1,6 +1,7 @@
import asyncio
from fastapi import APIRouter
from fastapi.staticfiles import StaticFiles
from lnbits.db import Database
from lnbits.helpers import template_renderer
@ -11,6 +12,14 @@ db = Database("ext_events")
events_ext: APIRouter = APIRouter(prefix="/events", tags=["Events"])
events_static_files = [
{
"path": "/events/static",
"app": StaticFiles(packages=[("lnbits", "extensions/events/static")]),
"name": "events_static",
}
]
def events_renderer():
return template_renderer(["lnbits/extensions/events/templates"])

View File

@ -1,6 +1,6 @@
{
"name": "Events",
"short_description": "Sell and register event tickets",
"icon": "local_activity",
"tile": "/events/static/image/events.png",
"contributors": ["benarc"]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

View File

@ -1,15 +1,6 @@
import asyncio
import json
from http import HTTPStatus
from urllib.parse import urlparse
import httpx
from fastapi import HTTPException
from loguru import logger
from lnbits import bolt11
from lnbits.core.models import Payment
from lnbits.core.services import pay_invoice
from lnbits.extensions.events.models import CreateTicket
from lnbits.helpers import get_current_extension_name
from lnbits.tasks import register_invoice_listener
@ -29,11 +20,17 @@ async def wait_for_paid_invoices():
async def on_invoice_paid(payment: Payment) -> None:
# (avoid loops)
if (
"events" == payment.extra.get("tag")
payment.extra
and "events" == payment.extra.get("tag")
and payment.extra.get("name")
and payment.extra.get("email")
):
CreateTicket.name = str(payment.extra.get("name"))
CreateTicket.email = str(payment.extra.get("email"))
await api_ticket_send_ticket(payment.memo, payment.payment_hash, CreateTicket)
await api_ticket_send_ticket(
payment.memo,
payment.payment_hash,
CreateTicket(
name=str(payment.extra.get("name")),
email=str(payment.extra.get("email")),
),
)
return

View File

@ -1,8 +1,7 @@
from datetime import date, datetime
from http import HTTPStatus
from fastapi import Request
from fastapi.params import Depends
from fastapi import Depends, Request
from fastapi.templating import Jinja2Templates
from starlette.exceptions import HTTPException
from starlette.responses import HTMLResponse

View File

@ -1,10 +1,7 @@
from http import HTTPStatus
from fastapi.param_functions import Query
from fastapi.params import Depends
from loguru import logger
from fastapi import Depends, Query
from starlette.exceptions import HTTPException
from starlette.requests import Request
from lnbits.core.crud import get_user
from lnbits.core.services import create_invoice
@ -38,7 +35,8 @@ async def api_events(
wallet_ids = [wallet.wallet.id]
if all_wallets:
wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
user = await get_user(wallet.wallet.user)
wallet_ids = user.wallet_ids if user else []
return [event.dict() for event in await get_events(wallet_ids)]
@ -92,7 +90,8 @@ async def api_tickets(
wallet_ids = [wallet.wallet.id]
if all_wallets:
wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
user = await get_user(wallet.wallet.user)
wallet_ids = user.wallet_ids if user else []
return [ticket.dict() for ticket in await get_tickets(wallet_ids)]
@ -119,26 +118,32 @@ async def api_ticket_make_ticket(event_id, name, email):
@events_ext.post("/api/v1/tickets/{event_id}/{payment_hash}")
async def api_ticket_send_ticket(event_id, payment_hash, data: CreateTicket):
event = await get_event(event_id)
try:
status = await api_payment(payment_hash)
if status["paid"]:
ticket = await create_ticket(
payment_hash=payment_hash,
wallet=event.wallet,
event=event_id,
name=data.name,
email=data.email,
if not event:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail=f"Event could not be fetched.",
)
status = await api_payment(payment_hash)
if status["paid"]:
exists = await get_ticket(payment_hash)
if exists:
return {"paid": True, "ticket_id": exists.id}
ticket = await create_ticket(
payment_hash=payment_hash,
wallet=event.wallet,
event=event_id,
name=data.name,
email=data.email,
)
if not ticket:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail=f"Event could not be fetched.",
)
if not ticket:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail=f"Event could not be fetched.",
)
return {"paid": True, "ticket_id": ticket.id}
except Exception:
raise HTTPException(status_code=HTTPStatus.NOT_FOUND, detail="Not paid")
return {"paid": True, "ticket_id": ticket.id}
return {"paid": False}

View File

@ -1,6 +1,6 @@
{
"name": "Build your own!!",
"short_description": "Join us, make an extension",
"icon": "info",
"tile": "/cashu/static/image/tile.png",
"contributors": ["github_username"]
}

View File

@ -1,5 +1,4 @@
from fastapi import FastAPI, Request
from fastapi.params import Depends
from fastapi import Depends, Request
from fastapi.templating import Jinja2Templates
from starlette.responses import HTMLResponse
@ -14,7 +13,7 @@ templates = Jinja2Templates(directory="templates")
@example_ext.get("/", response_class=HTMLResponse)
async def index(
request: Request,
user: User = Depends(check_user_exists), # type: ignore
user: User = Depends(check_user_exists),
):
return example_renderer().TemplateResponse(
"example/index.html", {"request": request, "user": user.dict()}

View File

@ -1,6 +1,6 @@
{
"name": "Gerty",
"short_description": "Desktop bitcoin Assistant",
"icon": "sentiment_satisfied",
"tile": "/gerty/static/gerty.png",
"contributors": ["arcbtc", "blackcoffeebtc"]
}

View File

@ -117,7 +117,7 @@ async def get_mempool_info(endPoint: str, gerty) -> dict:
mempool_id,
json.dumps(response.json()),
endPoint,
int(time.time()),
time.time(),
gerty.mempool_endpoint,
),
)
@ -129,7 +129,7 @@ async def get_mempool_info(endPoint: str, gerty) -> dict:
"UPDATE gerty.mempool SET data = ?, time = ? WHERE endpoint = ? AND mempool_endpoint = ?",
(
json.dumps(response.json()),
int(time.time()),
time.time(),
endPoint,
gerty.mempool_endpoint,
),

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@ -1,4 +1,5 @@
from fastapi import APIRouter
from fastapi.staticfiles import StaticFiles
from lnbits.db import Database
from lnbits.helpers import template_renderer
@ -12,4 +13,12 @@ def hivemind_renderer():
return template_renderer(["lnbits/extensions/hivemind/templates"])
hivemind_static_files = [
{
"path": "/hivemind/static",
"app": StaticFiles(packages=[("lnbits", "extensions/hivemind/static")]),
"name": "hivemind_static",
}
]
from .views import * # noqa

View File

@ -1,6 +1,6 @@
{
"name": "Hivemind",
"short_description": "Make cheap talk expensive!",
"icon": "batch_prediction",
"tile": "/hivemind/static/image/hivemind.png",
"contributors": ["fiatjaf"]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -1,6 +1,6 @@
{
"name": "Invoices",
"short_description": "Create invoices for your clients.",
"icon": "request_quote",
"tile": "/invoices/static/image/invoices.png",
"contributors": ["leesalminen"]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

@ -1,6 +1,6 @@
{
"name": "Spotify Jukebox",
"short_description": "Spotify jukebox middleware",
"icon": "radio",
"tile": "/jukebox/static/image/jukebox.png",
"contributors": ["benarc"]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -1,7 +1,6 @@
from http import HTTPStatus
from fastapi import Request
from fastapi.params import Depends
from fastapi import Depends, Request
from fastapi.templating import Jinja2Templates
from starlette.exceptions import HTTPException
from starlette.responses import HTMLResponse
@ -17,9 +16,7 @@ templates = Jinja2Templates(directory="templates")
@jukebox_ext.get("/", response_class=HTMLResponse)
async def index(
request: Request, user: User = Depends(check_user_exists) # type: ignore
):
async def index(request: Request, user: User = Depends(check_user_exists)):
return jukebox_renderer().TemplateResponse(
"jukebox/index.html", {"request": request, "user": user.dict()}
)

View File

@ -3,10 +3,9 @@ import json
from http import HTTPStatus
import httpx
from fastapi.param_functions import Query
from fastapi.params import Depends
from fastapi import Depends, Query
from starlette.exceptions import HTTPException
from starlette.responses import HTMLResponse # type: ignore
from starlette.responses import HTMLResponse
from lnbits.core.services import create_invoice
from lnbits.core.views.api import api_payment
@ -28,7 +27,7 @@ from .models import CreateJukeboxPayment, CreateJukeLinkData
@jukebox_ext.get("/api/v1/jukebox")
async def api_get_jukeboxs(
wallet: WalletTypeInfo = Depends(require_admin_key), # type: ignore
wallet: WalletTypeInfo = Depends(require_admin_key),
):
wallet_user = wallet.wallet.user

View File

@ -1,7 +1,7 @@
{
"name": "DJ Livestream",
"short_description": "Sell tracks and split revenue (lnurl-pay)",
"icon": "speaker",
"tile": "/livestream/static/image/livestream.png",
"contributors": [
"fiatjaf",
"cryptograffiti"

View File

@ -90,7 +90,7 @@ async def lnurl_callback(
wallet_id=ls.wallet,
amount=int(amount_received / 1000),
memo=await track.fullname(),
unhashed_description=(await track.lnurlpay_metadata()).encode("utf-8"),
unhashed_description=(await track.lnurlpay_metadata()).encode(),
extra={"tag": "livestream", "track": track.id, "comment": comment},
)

View File

@ -1,12 +1,12 @@
import json
from typing import Optional
from fastapi.params import Query
from fastapi import Query
from lnurl import Lnurl
from lnurl import encode as lnurl_encode # type: ignore
from lnurl.models import LnurlPaySuccessAction, UrlAction # type: ignore
from lnurl.types import LnurlPayMetadata # type: ignore
from pydantic.main import BaseModel
from pydantic import BaseModel
from starlette.requests import Request

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

View File

@ -1,6 +1,7 @@
import asyncio
from fastapi import APIRouter
from starlette.staticfiles import StaticFiles
from lnbits.db import Database
from lnbits.helpers import template_renderer
@ -10,6 +11,14 @@ db = Database("ext_lnaddress")
lnaddress_ext: APIRouter = APIRouter(prefix="/lnaddress", tags=["lnaddress"])
lnaddress_static_files = [
{
"path": "/lnaddress/static",
"app": StaticFiles(directory="lnbits/extensions/lnaddress/static"),
"name": "lnaddress_static",
}
]
def lnaddress_renderer():
return template_renderer(["lnbits/extensions/lnaddress/templates"])

View File

@ -1,6 +1,6 @@
{
"name": "Lightning Address",
"short_description": "Sell LN addresses for your domain",
"icon": "alternate_email",
"tile": "/lnaddress/static/image/lnaddress.png",
"contributors": ["talvasconcelos"]
}

View File

@ -72,7 +72,7 @@ async def lnurl_callback(address_id, amount: int = Query(...)):
"amount": int(amount_received / 1000),
"description_hash": (
await address.lnurlpay_metadata(domain=domain.domain)
).encode("utf-8"),
).encode(),
"extra": {"tag": f"Payment to {address.username}@{domain.domain}"},
},
timeout=40,

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -1,4 +1,5 @@
from fastapi import APIRouter
from starlette.staticfiles import StaticFiles
from lnbits.db import Database
from lnbits.helpers import template_renderer
@ -7,6 +8,14 @@ db = Database("ext_lndhub")
lndhub_ext: APIRouter = APIRouter(prefix="/lndhub", tags=["lndhub"])
lndhub_static_files = [
{
"path": "/lndhub/static",
"app": StaticFiles(directory="lnbits/extensions/lndhub/static"),
"name": "lndhub_static",
}
]
def lndhub_renderer():
return template_renderer(["lnbits/extensions/lndhub/templates"])

View File

@ -1,6 +1,6 @@
{
"name": "LndHub",
"short_description": "Access lnbits from BlueWallet or Zeus",
"icon": "navigation",
"tile": "/lndhub/static/image/lndhub.png",
"contributors": ["fiatjaf"]
}

View File

@ -23,7 +23,7 @@ async def check_wallet(
)
t = api_key_header_auth.split(" ")[1]
_, token = b64decode(t).decode("utf-8").split(":")
_, token = b64decode(t).decode().split(":")
return await get_key_type(r, api_key_header=token)

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -35,9 +35,9 @@ async def lndhub_auth(data: AuthData):
token = (
data.refresh_token
if data.refresh_token
else urlsafe_b64encode(
(data.login + ":" + data.password).encode("utf-8")
).decode("ascii")
else urlsafe_b64encode((data.login + ":" + data.password).encode()).decode(
"ascii"
)
)
return {"refresh_token": token, "access_token": token}

View File

@ -2,6 +2,7 @@ import asyncio
import json
from fastapi import APIRouter
from starlette.staticfiles import StaticFiles
from lnbits.db import Database
from lnbits.helpers import template_renderer
@ -11,6 +12,14 @@ db = Database("ext_lnticket")
lnticket_ext: APIRouter = APIRouter(prefix="/lnticket", tags=["LNTicket"])
lnticket_static_files = [
{
"path": "/lnticket/static",
"app": StaticFiles(directory="lnbits/extensions/lnticket/static"),
"name": "lnticket_static",
}
]
def lnticket_renderer():
return template_renderer(["lnbits/extensions/lnticket/templates"])

View File

@ -1,6 +1,6 @@
{
"name": "Support Tickets",
"short_description": "LN support ticket system",
"icon": "contact_support",
"tile": "/lnticket/static/image/lntickets.png",
"contributors": ["benarc"]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -19,7 +19,7 @@ async def wait_for_paid_invoices():
async def on_invoice_paid(payment: Payment) -> None:
if payment.extra.get("tag") != "lnticket":
if not payment.extra or payment.extra.get("tag") != "lnticket":
# not a lnticket invoice
return

View File

@ -33,6 +33,7 @@ async def display(request: Request, form_id):
)
wallet = await get_wallet(form.wallet)
assert wallet
return lnticket_renderer().TemplateResponse(
"lnticket/display.html",

View File

@ -1,8 +1,7 @@
import re
from http import HTTPStatus
from fastapi import Query
from fastapi.params import Depends
from fastapi import Depends, Query
from starlette.exceptions import HTTPException
from lnbits.core.crud import get_user
@ -35,7 +34,8 @@ async def api_forms_get(
wallet_ids = [wallet.wallet.id]
if all_wallets:
wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
user = await get_user(wallet.wallet.user)
wallet_ids = user.wallet_ids if user else []
return [form.dict() for form in await get_forms(wallet_ids)]
@ -91,7 +91,8 @@ async def api_tickets(
wallet_ids = [wallet.wallet.id]
if all_wallets:
wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
user = await get_user(wallet.wallet.user)
wallet_ids = user.wallet_ids if user else []
return [form.dict() for form in await get_tickets(wallet_ids)]

View File

@ -1,6 +1,7 @@
import asyncio
from fastapi import APIRouter
from starlette.staticfiles import StaticFiles
from lnbits.db import Database
from lnbits.helpers import template_renderer
@ -10,6 +11,14 @@ db = Database("ext_lnurldevice")
lnurldevice_ext: APIRouter = APIRouter(prefix="/lnurldevice", tags=["lnurldevice"])
lnurldevice_static_files = [
{
"path": "/lnurldevice/static",
"app": StaticFiles(directory="lnbits/extensions/lnurldevice/static"),
"name": "lnurldevice_static",
}
]
def lnurldevice_renderer():
return template_renderer(["lnbits/extensions/lnurldevice/templates"])

View File

@ -1,6 +1,6 @@
{
"name": "LNURLDevice",
"short_description": "For offline LNURL devices",
"icon": "point_of_sale",
"tile": "/lnurldevice/static/image/lnurldevice.png",
"contributors": ["arcbtc"]
}

View File

@ -246,7 +246,7 @@ async def lnurl_callback(
wallet_id=device.wallet,
amount=int(lnurldevicepayment.sats / 1000),
memo=device.id + " PIN " + str(lnurldevicepayment.pin),
unhashed_description=device.lnurlpay_metadata.encode("utf-8"),
unhashed_description=device.lnurlpay_metadata.encode(),
extra={
"tag": "Switch",
"pin": str(lnurldevicepayment.pin),
@ -267,7 +267,7 @@ async def lnurl_callback(
wallet_id=device.wallet,
amount=int(lnurldevicepayment.sats / 1000),
memo=device.title,
unhashed_description=device.lnurlpay_metadata.encode("utf-8"),
unhashed_description=device.lnurlpay_metadata.encode(),
extra={"tag": "PoS"},
)
lnurldevicepayment = await update_lnurldevicepayment(

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -1,7 +1,7 @@
{
"name": "LNURLp",
"short_description": "Make reusable LNURL pay links",
"icon": "receipt",
"tile": "/lnurlp/static/image/lnurl-pay.png",
"contributors": [
"arcbtc",
"eillarra",

View File

@ -1,18 +1,19 @@
from typing import List, Optional, Union
from lnbits.helpers import urlsafe_short_hash
from lnbits.db import SQLITE
from . import db
from .models import CreatePayLinkData, PayLink
async def create_pay_link(data: CreatePayLinkData, wallet_id: str) -> PayLink:
link_id = urlsafe_short_hash()
result = await db.execute(
returning = "" if db.type == SQLITE else "RETURNING ID"
method = db.execute if db.type == SQLITE else db.fetchone
result = await (method)(
f"""
INSERT INTO lnurlp.pay_links (
id,
wallet,
description,
min,
@ -28,11 +29,10 @@ async def create_pay_link(data: CreatePayLinkData, wallet_id: str) -> PayLink:
currency,
fiat_base_multiplier
)
VALUES (?, ?, ?, ?, ?, 0, 0, ?, ?, ?, ?, ?, ?, ?, ?)
VALUES (?, ?, ?, ?, 0, 0, ?, ?, ?, ?, ?, ?, ?, ?)
{returning}
""",
(
link_id,
wallet_id,
data.description,
data.min,
@ -47,6 +47,10 @@ async def create_pay_link(data: CreatePayLinkData, wallet_id: str) -> PayLink:
data.fiat_base_multiplier,
),
)
if db.type == SQLITE:
link_id = result._result_proxy.lastrowid
else:
link_id = result[0]
link = await get_pay_link(link_id)
assert link, "Newly created link couldn't be retrieved"

View File

@ -87,7 +87,7 @@ async def api_lnurl_callback(request: Request, link_id):
wallet_id=link.wallet,
amount=int(amount_received / 1000),
memo=link.description,
unhashed_description=link.lnurlpay_metadata.encode("utf-8"),
unhashed_description=link.lnurlpay_metadata.encode(),
extra={
"tag": "lnurlp",
"link": link.id,

View File

@ -68,76 +68,3 @@ async def m005_webhook_headers_and_body(db):
"""
await db.execute("ALTER TABLE lnurlp.pay_links ADD COLUMN webhook_headers TEXT;")
await db.execute("ALTER TABLE lnurlp.pay_links ADD COLUMN webhook_body TEXT;")
async def m006_redux(db):
"""
Add UUID ID's to links and migrates existing data
"""
await db.execute("ALTER TABLE lnurlp.pay_links RENAME TO pay_links_old")
await db.execute(
f"""
CREATE TABLE lnurlp.pay_links (
id TEXT PRIMARY KEY,
wallet TEXT NOT NULL,
description TEXT NOT NULL,
min INTEGER NOT NULL,
max INTEGER,
currency TEXT,
fiat_base_multiplier INTEGER DEFAULT 1,
served_meta INTEGER NOT NULL,
served_pr INTEGER NOT NULL,
webhook_url TEXT,
success_text TEXT,
success_url TEXT,
comment_chars INTEGER DEFAULT 0,
webhook_headers TEXT,
webhook_body TEXT
);
"""
)
for row in [
list(row) for row in await db.fetchall("SELECT * FROM lnurlp.pay_links_old")
]:
await db.execute(
"""
INSERT INTO lnurlp.pay_links (
id,
wallet,
description,
min,
served_meta,
served_pr,
webhook_url,
success_text,
success_url,
currency,
comment_chars,
max,
fiat_base_multiplier,
webhook_headers,
webhook_body
)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
row[0],
row[1],
row[2],
row[3],
row[4],
row[5],
row[6],
row[7],
row[8],
row[9],
row[10],
row[11],
row[12],
row[13],
row[14],
),
)
await db.execute("DROP TABLE lnurlp.pay_links_old")

View File

@ -26,7 +26,7 @@ class CreatePayLinkData(BaseModel):
class PayLink(BaseModel):
id: str
id: int
wallet: str
description: str
min: float

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -1,8 +1,7 @@
# type: ignore
from os import getenv
from fastapi import Request
from fastapi.params import Depends
from fastapi import Depends, Request
from fastapi.templating import Jinja2Templates
from pyngrok import conf, ngrok
@ -35,9 +34,7 @@ ngrok_tunnel = ngrok.connect(port)
@ngrok_ext.get("/")
async def index(
request: Request, user: User = Depends(check_user_exists) # type: ignore
):
async def index(request: Request, user: User = Depends(check_user_exists)):
return ngrok_renderer().TemplateResponse(
"ngrok/index.html", {"request": request, "ngrok": string5, "user": user.dict()}
)

View File

@ -1,6 +1,6 @@
{
"name": "Nostr NIP-5",
"short_description": "Verify addresses for Nostr NIP-5",
"icon": "request_quote",
"tile": "/nostrnip5/static/image/nostrnip5.png",
"contributors": ["leesalminen"]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -1,7 +1,7 @@
{
"name": "OfflineShop",
"short_description": "Receive payments for products offline!",
"icon": "nature_people",
"tile": "/offlineshop/static/image/offlineshop.png",
"contributors": [
"fiatjaf"
]

View File

@ -73,7 +73,7 @@ async def lnurl_callback(request: Request, item_id: int):
wallet_id=shop.wallet,
amount=int(amount_received / 1000),
memo=item.name,
unhashed_description=(await item.lnurlpay_metadata()).encode("utf-8"),
unhashed_description=(await item.lnurlpay_metadata()).encode(),
extra={"tag": "offlineshop", "item": item.id},
)
except Exception as exc:

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -1,4 +1,5 @@
from fastapi import APIRouter
from starlette.staticfiles import StaticFiles
from lnbits.db import Database
from lnbits.helpers import template_renderer
@ -7,6 +8,14 @@ db = Database("ext_paywall")
paywall_ext: APIRouter = APIRouter(prefix="/paywall", tags=["Paywall"])
paywall_static_files = [
{
"path": "/paywall/static",
"app": StaticFiles(directory="lnbits/extensions/paywall/static"),
"name": "paywall_static",
}
]
def paywall_renderer():
return template_renderer(["lnbits/extensions/paywall/templates"])

View File

@ -1,6 +1,6 @@
{
"name": "Paywall",
"short_description": "Create paywalls for content",
"icon": "policy",
"tile": "/paywall/static/image/paywall.png",
"contributors": ["eillarra"]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -19,7 +19,8 @@ async def api_paywalls(
wallet_ids = [wallet.wallet.id]
if all_wallets:
wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
user = await get_user(wallet.wallet.user)
wallet_ids = user.wallet_ids if user else []
return [paywall.dict() for paywall in await get_paywalls(wallet_ids)]
@ -57,6 +58,7 @@ async def api_paywall_create_invoice(
data: CreatePaywallInvoice, paywall_id: str = Query(None)
):
paywall = await get_paywall(paywall_id)
assert paywall
if data.amount < paywall.amount:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
@ -94,7 +96,9 @@ async def api_paywal_check_invoice(
if is_paid:
wallet = await get_wallet(paywall.wallet)
assert wallet
payment = await wallet.get_payment(payment_hash)
assert payment
await payment.set_pending(False)
return {"paid": True, "url": paywall.url, "remembers": paywall.remembers}

View File

@ -1,4 +1,5 @@
from fastapi import APIRouter
from starlette.staticfiles import StaticFiles
from lnbits.db import Database
from lnbits.helpers import template_renderer
@ -7,6 +8,14 @@ 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"])

View File

@ -1,6 +1,6 @@
{
"name": "Sats Dice",
"short_description": "LNURL Satoshi dice",
"icon": "casino",
"tile": "/satsdice/static/image/satsdice.png",
"contributors": ["arcbtc"]
}

View File

@ -76,7 +76,7 @@ async def api_lnurlp_callback(
wallet_id=link.wallet,
amount=int(amount_received / 1000),
memo="Satsdice bet",
unhashed_description=link.lnurlpay_metadata.encode("utf-8"),
unhashed_description=link.lnurlpay_metadata.encode(),
extra={"tag": "satsdice", "link": link.id, "comment": "comment"},
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -3,9 +3,7 @@ from http import HTTPStatus
from io import BytesIO
import pyqrcode
from fastapi import Request
from fastapi.param_functions import Query
from fastapi.params import Depends
from fastapi import Depends, Query, Request
from fastapi.templating import Jinja2Templates
from starlette.exceptions import HTTPException
from starlette.responses import HTMLResponse
@ -28,9 +26,7 @@ templates = Jinja2Templates(directory="templates")
@satsdice_ext.get("/", response_class=HTMLResponse)
async def index(
request: Request, user: User = Depends(check_user_exists) # type: ignore
):
async def index(request: Request, user: User = Depends(check_user_exists)):
return satsdice_renderer().TemplateResponse(
"satsdice/index.html", {"request": request, "user": user.dict()}
)
@ -108,7 +104,7 @@ async def displaywin(
data = CreateSatsDiceWithdraw(
satsdice_pay=satsdicelink.id,
value=paylink.value * satsdicelink.multiplier,
value=int(paylink.value * satsdicelink.multiplier),
payment_hash=payment_hash,
used=0,
)

View File

@ -1,8 +1,6 @@
from http import HTTPStatus
from fastapi import Request
from fastapi.param_functions import Query
from fastapi.params import Depends
from fastapi import Depends, Query, Request
from lnurl.exceptions import InvalidUrl as LnurlInvalidUrl # type: ignore
from starlette.exceptions import HTTPException
@ -26,7 +24,7 @@ from .models import CreateSatsDiceLink
@satsdice_ext.get("/api/v1/links")
async def api_links(
request: Request,
wallet: WalletTypeInfo = Depends(get_key_type), # type: ignore
wallet: WalletTypeInfo = Depends(get_key_type),
all_wallets: bool = Query(False),
):
wallet_ids = [wallet.wallet.id]
@ -49,7 +47,7 @@ async def api_links(
@satsdice_ext.get("/api/v1/links/{link_id}")
async def api_link_retrieve(
link_id: str = Query(None), wallet: WalletTypeInfo = Depends(get_key_type) # type: ignore
link_id: str = Query(None), wallet: WalletTypeInfo = Depends(get_key_type)
):
link = await get_satsdice_pay(link_id)
@ -70,7 +68,7 @@ async def api_link_retrieve(
@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), # type: ignore
wallet: WalletTypeInfo = Depends(get_key_type),
link_id: str = Query(None),
):
if data.min_bet > data.max_bet:
@ -98,7 +96,7 @@ async def api_link_create_or_update(
@satsdice_ext.delete("/api/v1/links/{link_id}")
async def api_link_delete(
wallet: WalletTypeInfo = Depends(get_key_type), # type: ignore
wallet: WalletTypeInfo = Depends(get_key_type),
link_id: str = Query(None),
):
link = await get_satsdice_pay(link_id)

Some files were not shown because too many files have changed in this diff Show More