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

This commit is contained in:
ben 2023-01-03 19:36:05 +00:00
commit 1b201086ba
32 changed files with 134 additions and 232 deletions

View file

@ -6,7 +6,7 @@ format: prettier isort black
check: mypy checkprettier checkisort checkblack
prettier: $(shell find lnbits -name "*.js" -name ".html")
prettier: $(shell find lnbits -name "*.js" -o -name ".html")
./node_modules/.bin/prettier --write lnbits/static/js/*.js lnbits/core/static/js/*.js lnbits/extensions/*/templates/*/*.html ./lnbits/core/templates/core/*.html lnbits/templates/*.html lnbits/extensions/*/static/js/*.js lnbits/extensions/*/static/components/*/*.js lnbits/extensions/*/static/components/*/*.html
black:
@ -18,7 +18,7 @@ mypy:
isort:
poetry run isort .
checkprettier: $(shell find lnbits -name "*.js" -name ".html")
checkprettier: $(shell find lnbits -name "*.js" -o -name ".html")
./node_modules/.bin/prettier --check lnbits/static/js/*.js lnbits/core/static/js/*.js lnbits/extensions/*/templates/*/*.html ./lnbits/core/templates/core/*.html lnbits/templates/*.html lnbits/extensions/*/static/js/*.js lnbits/extensions/*/static/components/*/*.js lnbits/extensions/*/static/components/*/*.html
checkblack:

View file

@ -5,7 +5,7 @@
};
outputs = { self, nixpkgs, poetry2nix }@inputs:
let
supportedSystems = [ "x86_64-linux" "aarch64-linux" ];
supportedSystems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ];
forSystems = systems: f:
nixpkgs.lib.genAttrs systems
(system: f system (import nixpkgs { inherit system; overlays = [ poetry2nix.overlay self.overlays.default ]; }));

View file

@ -1,7 +1,6 @@
import hashlib
import re
import time
from binascii import unhexlify
from decimal import Decimal
from typing import List, NamedTuple, Optional
@ -108,7 +107,7 @@ def decode(pr: str) -> Invoice:
message = bytearray([ord(c) for c in hrp]) + data.tobytes()
sig = signature[0:64]
if invoice.payee:
key = VerifyingKey.from_string(unhexlify(invoice.payee), curve=SECP256k1)
key = VerifyingKey.from_string(bytes.fromhex(invoice.payee), curve=SECP256k1)
key.verify(sig, message, hashlib.sha256, sigdecode=sigdecode_string)
else:
keys = VerifyingKey.from_public_key_recovery(
@ -131,7 +130,7 @@ def encode(options):
if options["timestamp"]:
addr.date = int(options["timestamp"])
addr.paymenthash = unhexlify(options["paymenthash"])
addr.paymenthash = bytes.fromhex(options["paymenthash"])
if options["description"]:
addr.tags.append(("d", options["description"]))
@ -149,8 +148,8 @@ def encode(options):
while len(splits) >= 5:
route.append(
(
unhexlify(splits[0]),
unhexlify(splits[1]),
bytes.fromhex(splits[0]),
bytes.fromhex(splits[1]),
int(splits[2]),
int(splits[3]),
int(splits[4]),
@ -235,7 +234,7 @@ def lnencode(addr, privkey):
raise ValueError("Must include either 'd' or 'h'")
# We actually sign the hrp, then data (padded to 8 bits with zeroes).
privkey = secp256k1.PrivateKey(bytes(unhexlify(privkey)))
privkey = secp256k1.PrivateKey(bytes.fromhex(privkey))
sig = privkey.ecdsa_sign_recoverable(
bytearray([ord(c) for c in hrp]) + data.tobytes()
)
@ -261,7 +260,7 @@ class LnAddr(object):
def __str__(self):
return "LnAddr[{}, amount={}{} tags=[{}]]".format(
hexlify(self.pubkey.serialize()).decode("utf-8"),
bytes.hex(self.pubkey.serialize()).decode("utf-8"),
self.amount,
self.currency,
", ".join([k + "=" + str(v) for k, v in self.tags]),

View file

@ -1,6 +1,5 @@
import asyncio
import json
from binascii import unhexlify
from io import BytesIO
from typing import Dict, List, Optional, Tuple
from urllib.parse import parse_qs, urlparse
@ -303,7 +302,7 @@ async def perform_lnurlauth(
) -> Optional[LnurlErrorResponse]:
cb = urlparse(callback)
k1 = unhexlify(parse_qs(cb.query)["k1"][0])
k1 = bytes.fromhex(parse_qs(cb.query)["k1"][0])
key = wallet.wallet.lnurlauth_key(cb.netloc)

View file

@ -1,5 +1,4 @@
import asyncio
import binascii
import hashlib
import json
import time
@ -142,16 +141,14 @@ async def api_payments_create_invoice(data: CreateInvoiceData, wallet: Wallet):
if data.description_hash or data.unhashed_description:
try:
description_hash = (
binascii.unhexlify(data.description_hash)
if data.description_hash
else b""
bytes.fromhex(data.description_hash) if data.description_hash else b""
)
unhashed_description = (
binascii.unhexlify(data.unhashed_description)
bytes.fromhex(data.unhashed_description)
if data.unhashed_description
else b""
)
except binascii.Error:
except ValueError:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
detail="'description_hash' and 'unhashed_description' must be a valid hex strings",

View file

@ -2,7 +2,6 @@ import base64
import hashlib
import hmac
import urllib
from binascii import unhexlify
from http import HTTPStatus
from typing import Dict
@ -19,7 +18,7 @@ def generate_bleskomat_lnurl_signature(
payload: str, api_key_secret: str, api_key_encoding: str = "hex"
):
if api_key_encoding == "hex":
key = unhexlify(api_key_secret)
key = bytes.fromhex(api_key_secret)
elif api_key_encoding == "base64":
key = base64.b64decode(api_key_secret)
else:

View file

@ -42,23 +42,20 @@ Updated for v0.1.3
- Or you can Click the "KEYS / AUTH LINK" button to copy the auth URL to the clipboard. Then paste it into the Android app (Create Bolt Card -> PASTE AUTH URL).
- Click WRITE CARD NOW and approach the NFC card to set it up. DO NOT REMOVE THE CARD PREMATURELY!
## Rewriting / Erasing the card - Boltcard NFC Card Creator
## Erasing the card - Boltcard NFC Card Creator
Updated for v0.1.3
It is possible not only to reset the keys but also to disable the SUN function and completely erase the card so it can be used again as a static tag or set up as a new Bolt Card.
Since v0.1.2 of Boltcard NFC Card Creator it is possible not only reset the keys but also disable the SUN function and do the complete erase so the card can be use again as a static tag (or set as a new Bolt Card, ofc).
IMPORTANT:
* It is immanent that you have access to your old keys so do not erase this card in LNbits before you copied those strings!
* If you tried to write to them and failed you will need the same amount of positive writing sessions to unlock the card.
- in the BoltCard-Extension click the QR code button next to your old card and copy Key0
- in the BoltApp click Advanced - Reset keys and paste the Key0 into the first field named Key0
- repeat with Key1/Key2/Key3/Key0
- when done pasting all 4 keys scan your card with the BoltApp
- Thats it 🥳
- If everything was successful the card can be safely deleted from LNbits (but keep the keys backed up anyway; batter safe than brick).
You can watch a video of this process here https://www.youtube.com/watch?time_continue=230&v=Pe0YXHawHvQ&feature=emb_logo
- Click the QR code button next to a card to view its details and select WIPE
- OR click the red cross icon on the right side to reach the same
- In the android app (Advanced -> Reset Keys)
- Click SCAN QR CODE to scan the QR
- Or click WIPE DATA in LNbits to copy and paste in to the app (PASTE KEY JSON)
- Click RESET CARD NOW and approach the NFC card to erase it. DO NOT REMOVE THE CARD PREMATURELY!
- Now if there is all success the card can be safely delete from LNbits (but keep the keys backuped anyway; batter safe than brick).
If you somehow find yourself in some non-standard state (for instance only k3 and k4 remains filled after previous unsuccessful reset), then you need edit the key fields manually (for instance leave k0-k2 to zeroes and provide the right k3 and k4).
## Setting the card - computer (hard way)

View file

@ -147,7 +147,7 @@ async def api_hits(
@boltcards_ext.get("/api/v1/refunds")
async def api_hits(
async def api_refunds(
g: WalletTypeInfo = Depends(get_key_type), all_wallets: bool = Query(False)
):
wallet_ids = [g.wallet.id]

View file

@ -1,6 +1,5 @@
import asyncio
import os
from binascii import hexlify, unhexlify
from hashlib import sha256
from typing import Awaitable, Union
@ -56,7 +55,7 @@ async def create_swap(data: CreateSubmarineSwap) -> SubmarineSwap:
raise
refund_privkey = ec.PrivateKey(os.urandom(32), True, net)
refund_pubkey_hex = hexlify(refund_privkey.sec()).decode("UTF-8")
refund_pubkey_hex = bytes.hex(refund_privkey.sec()).decode("UTF-8")
res = req_wrap(
"post",
@ -121,7 +120,7 @@ async def create_reverse_swap(
return False
claim_privkey = ec.PrivateKey(os.urandom(32), True, net)
claim_pubkey_hex = hexlify(claim_privkey.sec()).decode("UTF-8")
claim_pubkey_hex = bytes.hex(claim_privkey.sec()).decode("UTF-8")
preimage = os.urandom(32)
preimage_hash = sha256(preimage).hexdigest()
@ -311,12 +310,12 @@ async def create_onchain_tx(
sequence = 0xFFFFFFFE
else:
privkey = ec.PrivateKey.from_wif(swap.claim_privkey)
preimage = unhexlify(swap.preimage)
preimage = bytes.fromhex(swap.preimage)
onchain_address = swap.onchain_address
sequence = 0xFFFFFFFF
locktime = swap.timeout_block_height
redeem_script = unhexlify(swap.redeem_script)
redeem_script = bytes.fromhex(swap.redeem_script)
fees = get_fee_estimation()
@ -324,7 +323,7 @@ async def create_onchain_tx(
script_pubkey = script.address_to_scriptpubkey(onchain_address)
vin = [TransactionInput(unhexlify(txid), vout_cnt, sequence=sequence)]
vin = [TransactionInput(bytes.fromhex(txid), vout_cnt, sequence=sequence)]
vout = [TransactionOutput(vout_amount - fees, script_pubkey)]
tx = Transaction(vin=vin, vout=vout)

View file

@ -1,6 +1,5 @@
import asyncio
import json
from binascii import hexlify
import httpx
import websockets
@ -84,7 +83,7 @@ def get_mempool_blockheight() -> int:
async def send_onchain_tx(tx: Transaction):
raw = hexlify(tx.serialize())
raw = bytes.hex(tx.serialize())
logger.debug(f"Boltz - mempool sending onchain tx...")
req_wrap(
"post",

View file

@ -1,7 +1,6 @@
import os
import random
import time
from binascii import hexlify, unhexlify
from typing import Any, List, Optional, Union
from cashu.core.base import MintKeyset

View file

@ -301,34 +301,6 @@ def gerty_should_sleep(utc_offset: int = 0):
return False
def get_date_suffix(dayNumber):
if 4 <= dayNumber <= 20 or 24 <= dayNumber <= 30:
return "th"
else:
return ["st", "nd", "rd"][dayNumber % 10 - 1]
def get_time_remaining(seconds, granularity=2):
intervals = (
# ('weeks', 604800), # 60 * 60 * 24 * 7
("days", 86400), # 60 * 60 * 24
("hours", 3600), # 60 * 60
("minutes", 60),
("seconds", 1),
)
result = []
for name, count in intervals:
value = seconds // count
if value:
seconds -= value * count
if value == 1:
name = name.rstrip("s")
result.append("{} {}".format(round(value), name))
return ", ".join(result[:granularity])
async def get_mining_stat(stat_slug: str, gerty):
text = []
if stat_slug == "mining_current_hash_rate":

View file

@ -30,7 +30,7 @@ async def index(request: Request, user: User = Depends(check_user_exists)):
@invoices_ext.get("/pay/{invoice_id}", response_class=HTMLResponse)
async def index(request: Request, invoice_id: str):
async def pay(request: Request, invoice_id: str):
invoice = await get_invoice(invoice_id)
if not invoice:

View file

@ -1,10 +1,8 @@
from binascii import unhexlify
from lnbits.bolt11 import Invoice
def to_buffer(payment_hash: str):
return {"type": "Buffer", "data": [b for b in unhexlify(payment_hash)]}
return {"type": "Buffer", "data": [b for b in bytes.fromhex(payment_hash)]}
def decoded_as_lndhub(invoice: Invoice):

View file

@ -71,7 +71,7 @@ async def get_all_addresses(wallet_ids: Union[str, List[str]]) -> List[Address]:
q = ",".join(["?"] * len(wallet_ids))
rows = await db.fetchall(
f"""
SELECT a.*
SELECT a.*
FROM nostrnip5.addresses a
JOIN nostrnip5.domains d ON d.id = a.domain_id
WHERE d.wallet IN ({q})
@ -139,7 +139,7 @@ async def delete_domain(domain_id) -> bool:
return True
async def delete_address(address_id) -> bool:
async def delete_address(address_id):
await db.execute(
"""
DELETE FROM nostrnip5.addresses WHERE id = ?

View file

@ -1,9 +1,9 @@
import asyncio
import json
from loguru import logger
from lnbits.core.models import Payment
from lnbits.helpers import urlsafe_short_hash
from lnbits.tasks import internal_invoice_queue, register_invoice_listener
from lnbits.tasks import register_invoice_listener
from .crud import activate_address
@ -18,17 +18,18 @@ async def wait_for_paid_invoices():
async def on_invoice_paid(payment: Payment) -> None:
if not payment.extra:
return
if payment.extra.get("tag") != "nostrnip5":
# not relevant
return
domain_id = payment.extra.get("domain_id")
address_id = payment.extra.get("address_id")
print("Activating NOSTR NIP-05")
print(domain_id)
print(address_id)
active = await activate_address(domain_id, address_id)
if domain_id and address_id:
logger.info("Activating NOSTR NIP-05")
logger.info(domain_id)
logger.info(address_id)
await activate_address(domain_id, address_id)
return

View file

@ -1,8 +1,7 @@
from datetime import datetime
from http import HTTPStatus
from fastapi import FastAPI, 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
@ -24,7 +23,7 @@ async def index(request: Request, user: User = Depends(check_user_exists)):
@nostrnip5_ext.get("/signup/{domain_id}", response_class=HTMLResponse)
async def index(request: Request, domain_id: str):
async def signup(request: Request, domain_id: str):
domain = await get_domain(domain_id)
if not domain:
@ -43,7 +42,7 @@ async def index(request: Request, domain_id: str):
@nostrnip5_ext.get("/rotate/{domain_id}/{address_id}", response_class=HTMLResponse)
async def index(request: Request, domain_id: str, address_id: str):
async def rotate(request: Request, domain_id: str, address_id: str):
domain = await get_domain(domain_id)
address = await get_address(domain_id, address_id)

View file

@ -1,10 +1,8 @@
import re
from http import HTTPStatus
from typing import Optional
from bech32 import bech32_decode, convertbits
from fastapi import Query, Request, Response
from fastapi.params import Depends
from fastapi import Depends, Query, Response
from loguru import logger
from starlette.exceptions import HTTPException
@ -38,7 +36,10 @@ async def api_domains(
):
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)
if not user:
return []
wallet_ids = user.wallet_ids
return [domain.dict() for domain in await get_domains(wallet_ids)]
@ -49,13 +50,20 @@ async def api_addresses(
):
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)
if not user:
return []
wallet_ids = user.wallet_ids
return [address.dict() for address in await get_all_addresses(wallet_ids)]
@nostrnip5_ext.get("/api/v1/domain/{domain_id}", status_code=HTTPStatus.OK)
async def api_invoice(domain_id: str, wallet: WalletTypeInfo = Depends(get_key_type)):
@nostrnip5_ext.get(
"/api/v1/domain/{domain_id}",
status_code=HTTPStatus.OK,
dependencies=[Depends(get_key_type)],
)
async def api_invoice(domain_id: str):
domain = await get_domain(domain_id)
if not domain:
raise HTTPException(
@ -104,11 +112,11 @@ async def api_address_delete(
@nostrnip5_ext.post(
"/api/v1/domain/{domain_id}/address/{address_id}/activate",
status_code=HTTPStatus.OK,
dependencies=[Depends(require_admin_key)],
)
async def api_address_activate(
domain_id: str,
address_id: str,
wallet: WalletTypeInfo = Depends(require_admin_key),
):
await activate_address(domain_id, address_id)
@ -126,9 +134,11 @@ async def api_address_rotate(
):
if post_data.pubkey.startswith("npub"):
hrp, data = bech32_decode(post_data.pubkey)
decoded_data = convertbits(data, 5, 8, False)
post_data.pubkey = bytes(decoded_data).hex()
_, data = bech32_decode(post_data.pubkey)
if data:
decoded_data = convertbits(data, 5, 8, False)
if decoded_data:
post_data.pubkey = bytes(decoded_data).hex()
if len(bytes.fromhex(post_data.pubkey)) != 32:
raise HTTPException(
@ -173,10 +183,12 @@ async def api_address_create(
status_code=HTTPStatus.NOT_FOUND, detail="Local part already exists."
)
if post_data.pubkey.startswith("npub"):
hrp, data = bech32_decode(post_data.pubkey)
decoded_data = convertbits(data, 5, 8, False)
post_data.pubkey = bytes(decoded_data).hex()
if post_data and post_data.pubkey.startswith("npub"):
_, data = bech32_decode(post_data.pubkey)
if data:
decoded_data = convertbits(data, 5, 8, False)
if decoded_data:
post_data.pubkey = bytes(decoded_data).hex()
if len(bytes.fromhex(post_data.pubkey)) != 32:
raise HTTPException(
@ -233,15 +245,17 @@ async def api_get_nostr_json(
output = {}
for address in addresses:
local_part = address.get("local_part").lower()
local_part = address.get("local_part")
if not local_part:
continue
if address.get("active") == False:
continue
if name and name.lower() != local_part:
if name and name.lower() != local_part.lower():
continue
output[local_part] = address.get("pubkey")
output[local_part.lower()] = address.get("pubkey")
response.headers["Access-Control-Allow-Origin"] = "*"
response.headers["Access-Control-Allow-Methods"] = "GET,OPTIONS"

View file

@ -131,53 +131,6 @@ async def check_address_balance(charge_id: str) -> Optional[Charges]:
################## SETTINGS ###################
async def save_theme(data: SatsPayThemes, css_id: str = None):
# insert or update
if css_id:
await db.execute(
"""
UPDATE satspay.themes SET custom_css = ?, title = ? WHERE css_id = ?
""",
(data.custom_css, data.title, css_id),
)
else:
css_id = urlsafe_short_hash()
await db.execute(
"""
INSERT INTO satspay.themes (
css_id,
title,
user,
custom_css
)
VALUES (?, ?, ?, ?)
""",
(
css_id,
data.title,
data.user,
data.custom_css,
),
)
return await get_theme(css_id)
async def get_theme(css_id: str) -> SatsPayThemes:
row = await db.fetchone("SELECT * FROM satspay.themes WHERE css_id = ?", (css_id,))
return SatsPayThemes.from_row(row) if row else None
async def get_themes(user_id: str) -> List[SatsPayThemes]:
rows = await db.fetchall(
"""SELECT * FROM satspay.themes WHERE "user" = ? ORDER BY "timestamp" DESC """,
(user_id,),
)
return await get_config(row.user)
################## SETTINGS ###################
async def save_theme(data: SatsPayThemes, css_id: str = None):
# insert or update
if css_id:

View file

@ -171,7 +171,7 @@ async def api_themes_retrieve(wallet: WalletTypeInfo = Depends(get_key_type)):
@satspay_ext.delete("/api/v1/themes/{theme_id}")
async def api_charge_delete(theme_id, wallet: WalletTypeInfo = Depends(get_key_type)):
async def api_theme_delete(theme_id, wallet: WalletTypeInfo = Depends(get_key_type)):
theme = await get_theme(theme_id)
if not theme:

View file

@ -25,9 +25,9 @@ async def wait_for_paid_invoices():
await on_invoice_paid(payment)
async def on_invoice_paid(payment: Payment) -> None:
async def on_invoice_paid(payment: Payment):
# (avoid loops)
if payment.extra.get("tag") == "scrubed":
if payment.extra and payment.extra.get("tag") == "scrubed":
# already scrubbed
return
@ -53,7 +53,7 @@ async def on_invoice_paid(payment: Payment) -> None:
timeout=40,
)
if r.is_error:
raise httpx.ConnectError
raise httpx.ConnectError("issue with scrub callback")
except (httpx.ConnectError, httpx.RequestError):
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,

View file

@ -1,5 +1,4 @@
from fastapi import Request
from fastapi.params import Depends
from fastapi import Depends, Request
from fastapi.templating import Jinja2Templates
from starlette.responses import HTMLResponse

View file

@ -1,9 +1,6 @@
from http import HTTPStatus
from fastapi import Request
from fastapi.param_functions import Query
from fastapi.params import Depends
from lnurl.exceptions import InvalidUrl as LnurlInvalidUrl # type: ignore
from fastapi import Depends, Query
from starlette.exceptions import HTTPException
from lnbits.core.crud import get_user
@ -23,14 +20,14 @@ from .models import CreateScrubLink
@scrub_ext.get("/api/v1/links", status_code=HTTPStatus.OK)
async def api_links(
req: Request,
wallet: WalletTypeInfo = Depends(get_key_type),
all_wallets: bool = Query(False),
):
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 []
try:
return [link.dict() for link in await get_scrub_links(wallet_ids)]
@ -43,9 +40,7 @@ async def api_links(
@scrub_ext.get("/api/v1/links/{link_id}", status_code=HTTPStatus.OK)
async def api_link_retrieve(
r: Request, link_id, wallet: WalletTypeInfo = Depends(get_key_type)
):
async def api_link_retrieve(link_id, wallet: WalletTypeInfo = Depends(get_key_type)):
link = await get_scrub_link(link_id)
if not link:

View file

@ -8,7 +8,7 @@ from .models import Tip, TipJar, createTipJar
async def create_tip(
id: int, wallet: str, message: str, name: str, sats: int, tipjar: str
id: str, wallet: str, message: str, name: str, sats: int, tipjar: str
) -> Tip:
"""Create a new Tip"""
await db.execute(
@ -33,11 +33,7 @@ async def create_tip(
async def create_tipjar(data: createTipJar) -> TipJar:
"""Create a new TipJar"""
returning = "" if db.type == SQLITE else "RETURNING ID"
method = db.execute if db.type == SQLITE else db.fetchone
result = await (method)(
await db.execute(
f"""
INSERT INTO tipjar.TipJars (
name,
@ -46,16 +42,11 @@ async def create_tipjar(data: createTipJar) -> TipJar:
onchain
)
VALUES (?, ?, ?, ?)
{returning}
""",
(data.name, data.wallet, data.webhook, data.onchain),
)
if db.type == SQLITE:
tipjar_id = result._result_proxy.lastrowid
else:
tipjar_id = result[0]
tipjar = await get_tipjar(tipjar_id)
row = await db.fetchone("SELECT * FROM tipjar.TipJars LIMIT 1")
tipjar = TipJar(**row)
assert tipjar
return tipjar

View file

@ -1,20 +1,7 @@
from sqlite3 import Row
from typing import Optional
from fastapi.param_functions import Query
from pydantic import BaseModel
from pydantic.main import BaseModel
class CreateCharge(BaseModel):
onchainwallet: str = Query(None)
lnbitswallet: str = Query(None)
description: str = Query(...)
webhook: str = Query(None)
completelink: str = Query(None)
completelinktext: str = Query(None)
time: int = Query(..., ge=1)
amount: int = Query(..., ge=1)
class createTip(BaseModel):
@ -44,8 +31,8 @@ class Tip(BaseModel):
class createTipJar(BaseModel):
name: str
wallet: str
webhook: str = None
onchain: str = None
webhook: Optional[str]
onchain: Optional[str]
class createTips(BaseModel):

View file

@ -1,8 +1,7 @@
from http import HTTPStatus
from fastapi import Request
from fastapi import Depends, Request
from fastapi.param_functions import Query
from fastapi.params import Depends
from fastapi.templating import Jinja2Templates
from starlette.exceptions import HTTPException

View file

@ -1,13 +1,13 @@
from http import HTTPStatus
from fastapi.param_functions import Query
from fastapi.params import Depends
from fastapi import Depends, Query
from starlette.exceptions import HTTPException
from lnbits.core.crud import get_user
from lnbits.decorators import WalletTypeInfo, get_key_type
from ..satspay.crud import create_charge
from ..satspay.models import CreateCharge
from . import tipjar_ext
from .crud import (
create_tip,
@ -22,7 +22,7 @@ from .crud import (
update_tipjar,
)
from .helpers import get_charge_details
from .models import CreateCharge, createTipJar, createTips
from .models import createTip, createTipJar, createTips
@tipjar_ext.post("/api/v1/tipjars")
@ -43,12 +43,16 @@ async def user_from_wallet(wallet: WalletTypeInfo = Depends(get_key_type)):
@tipjar_ext.post("/api/v1/tips")
async def api_create_tip(data: createTips):
"""Take data from tip form and return satspay charge"""
sats = data.sats
sats = int(data.sats)
message = data.message
if not message:
message = "No message"
tipjar_id = data.tipjar
tipjar_id = int(data.tipjar)
tipjar = await get_tipjar(tipjar_id)
if not tipjar:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND, detail="Tipjar does not exist."
)
webhook = tipjar.webhook
charge_details = await get_charge_details(tipjar.id)
@ -62,13 +66,14 @@ async def api_create_tip(data: createTips):
user=charge_details["user"],
data=CreateCharge(
amount=sats,
webhook=webhook,
webhook=webhook or "",
description=description,
onchainwallet=charge_details["onchainwallet"],
lnbitswallet=charge_details["lnbitswallet"],
completelink=charge_details["completelink"],
completelinktext=charge_details["completelinktext"],
time=charge_details["time"],
custom_css="",
),
)
@ -77,7 +82,7 @@ async def api_create_tip(data: createTips):
wallet=tipjar.wallet,
message=message,
name=name,
sats=data.sats,
sats=int(data.sats),
tipjar=data.tipjar,
)
@ -87,28 +92,34 @@ async def api_create_tip(data: createTips):
@tipjar_ext.get("/api/v1/tipjars")
async def api_get_tipjars(wallet: WalletTypeInfo = Depends(get_key_type)):
"""Return list of all tipjars assigned to wallet with given invoice key"""
wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
user = await get_user(wallet.wallet.user)
if not user:
return []
tipjars = []
for wallet_id in wallet_ids:
for wallet_id in user.wallet_ids:
new_tipjars = await get_tipjars(wallet_id)
tipjars += new_tipjars if new_tipjars else []
return [tipjar.dict() for tipjar in tipjars] if tipjars else []
return [tipjar.dict() for tipjar in tipjars]
@tipjar_ext.get("/api/v1/tips")
async def api_get_tips(wallet: WalletTypeInfo = Depends(get_key_type)):
"""Return list of all tips assigned to wallet with given invoice key"""
wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
user = await get_user(wallet.wallet.user)
if not user:
return []
tips = []
for wallet_id in wallet_ids:
for wallet_id in user.wallet_ids:
new_tips = await get_tips(wallet_id)
tips += new_tips if new_tips else []
return [tip.dict() for tip in tips] if tips else []
return [tip.dict() for tip in tips]
@tipjar_ext.put("/api/v1/tips/{tip_id}")
async def api_update_tip(
wallet: WalletTypeInfo = Depends(get_key_type), tip_id: str = Query(None)
data: createTip,
wallet: WalletTypeInfo = Depends(get_key_type),
tip_id: str = Query(None),
):
"""Update a tip with the data given in the request"""
if tip_id:
@ -125,7 +136,7 @@ async def api_update_tip(
status_code=HTTPStatus.FORBIDDEN, detail="Not your tip."
)
tip = await update_tip(tip_id, **g.data)
tip = await update_tip(tip_id, **data.dict())
else:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST, detail="No tip ID specified"
@ -135,7 +146,9 @@ async def api_update_tip(
@tipjar_ext.put("/api/v1/tipjars/{tipjar_id}")
async def api_update_tipjar(
wallet: WalletTypeInfo = Depends(get_key_type), tipjar_id: str = Query(None)
data: createTipJar,
wallet: WalletTypeInfo = Depends(get_key_type),
tipjar_id: int = Query(None),
):
"""Update a tipjar with the data given in the request"""
if tipjar_id:
@ -151,7 +164,7 @@ async def api_update_tipjar(
status_code=HTTPStatus.FORBIDDEN, detail="Not your tipjar."
)
tipjar = await update_tipjar(tipjar_id, **data)
tipjar = await update_tipjar(str(tipjar_id), **data.dict())
else:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST, detail="No tipjar ID specified"
@ -181,7 +194,7 @@ async def api_delete_tip(
@tipjar_ext.delete("/api/v1/tipjars/{tipjar_id}")
async def api_delete_tipjar(
wallet: WalletTypeInfo = Depends(get_key_type), tipjar_id: str = Query(None)
wallet: WalletTypeInfo = Depends(get_key_type), tipjar_id: int = Query(None)
):
"""Delete the tipjar with the given tipjar_id"""
tipjar = await get_tipjar(tipjar_id)

View file

@ -292,7 +292,7 @@ async def api_psbt_create(
@watchonly_ext.put("/api/v1/psbt/utxos")
async def api_psbt_extract_tx(
async def api_psbt_utxos_tx(
req: Request, w: WalletTypeInfo = Depends(require_admin_key)
):
"""Extract previous unspent transaction outputs (tx_id, vout) from PSBT"""

View file

@ -117,7 +117,7 @@ async def print_qr(request: Request, link_id):
@withdraw_ext.get("/csv/{link_id}", response_class=HTMLResponse)
async def print_qr(request: Request, link_id):
async def csv(request: Request, link_id):
link = await get_withdraw_link(link_id)
if not link:
raise HTTPException(

View file

@ -8,7 +8,6 @@ except ImportError: # pragma: nocover
import asyncio
import base64
import binascii
import hashlib
from os import environ, error
from typing import AsyncGenerator, Dict, Optional
@ -229,8 +228,8 @@ class LndWallet(Wallet):
try:
r_hash = hex_to_bytes(checking_id)
if len(r_hash) != 32:
raise binascii.Error
except binascii.Error:
raise ValueError
except ValueError:
# this may happen if we switch between backend wallets
# that use different checking_id formats
return PaymentStatus(None)
@ -250,8 +249,8 @@ class LndWallet(Wallet):
try:
r_hash = hex_to_bytes(checking_id)
if len(r_hash) != 32:
raise binascii.Error
except binascii.Error:
raise ValueError
except ValueError:
# this may happen if we switch between backend wallets
# that use different checking_id formats
return PaymentStatus(None)

View file

@ -94,7 +94,6 @@ exclude = """(?x)(
| ^lnbits/extensions/boltcards.
| ^lnbits/extensions/events.
| ^lnbits/extensions/gerty.
| ^lnbits/extensions/hivemind.
| ^lnbits/extensions/invoices.
| ^lnbits/extensions/livestream.
| ^lnbits/extensions/lnaddress.
@ -102,15 +101,11 @@ exclude = """(?x)(
| ^lnbits/extensions/lnticket.
| ^lnbits/extensions/lnurldevice.
| ^lnbits/extensions/lnurlp.
| ^lnbits/extensions/lnurlpayout.
| ^lnbits/extensions/nostrnip5.
| ^lnbits/extensions/offlineshop.
| ^lnbits/extensions/paywall.
| ^lnbits/extensions/satspay.
| ^lnbits/extensions/scrub.
| ^lnbits/extensions/splitpayments.
| ^lnbits/extensions/streamalerts.
| ^lnbits/extensions/tipjar.
| ^lnbits/extensions/tpos.
| ^lnbits/extensions/watchonly.
| ^lnbits/extensions/withdraw.

View file

@ -1,5 +1,4 @@
import hashlib
from binascii import hexlify
import pytest
import pytest_asyncio