mirror of
https://github.com/lnbits/lnbits.git
synced 2025-06-13 10:20:51 +02:00
black formating
This commit is contained in:
parent
7c4ce9bf96
commit
f8400512f7
1
conv.py
1
conv.py
@ -1,6 +1,7 @@
|
|||||||
import psycopg2
|
import psycopg2
|
||||||
import sqlite3
|
import sqlite3
|
||||||
import os
|
import os
|
||||||
|
|
||||||
# Python script to migrate an LNbits SQLite DB to Postgres
|
# Python script to migrate an LNbits SQLite DB to Postgres
|
||||||
# All credits to @Fritz446 for the awesome work
|
# All credits to @Fritz446 for the awesome work
|
||||||
|
|
||||||
|
@ -165,7 +165,7 @@ def lnencode(addr, privkey):
|
|||||||
if addr.amount:
|
if addr.amount:
|
||||||
amount = Decimal(str(addr.amount))
|
amount = Decimal(str(addr.amount))
|
||||||
# We can only send down to millisatoshi.
|
# We can only send down to millisatoshi.
|
||||||
if amount * 10 ** 12 % 10:
|
if amount * 10**12 % 10:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"Cannot encode {}: too many decimal places".format(addr.amount)
|
"Cannot encode {}: too many decimal places".format(addr.amount)
|
||||||
)
|
)
|
||||||
@ -270,7 +270,7 @@ class LnAddr(object):
|
|||||||
def shorten_amount(amount):
|
def shorten_amount(amount):
|
||||||
"""Given an amount in bitcoin, shorten it"""
|
"""Given an amount in bitcoin, shorten it"""
|
||||||
# Convert to pico initially
|
# Convert to pico initially
|
||||||
amount = int(amount * 10 ** 12)
|
amount = int(amount * 10**12)
|
||||||
units = ["p", "n", "u", "m", ""]
|
units = ["p", "n", "u", "m", ""]
|
||||||
for unit in units:
|
for unit in units:
|
||||||
if amount % 1000 == 0:
|
if amount % 1000 == 0:
|
||||||
@ -289,7 +289,7 @@ def _unshorten_amount(amount: str) -> int:
|
|||||||
# * `u` (micro): multiply by 0.000001
|
# * `u` (micro): multiply by 0.000001
|
||||||
# * `n` (nano): multiply by 0.000000001
|
# * `n` (nano): multiply by 0.000000001
|
||||||
# * `p` (pico): multiply by 0.000000000001
|
# * `p` (pico): multiply by 0.000000000001
|
||||||
units = {"p": 10 ** 12, "n": 10 ** 9, "u": 10 ** 6, "m": 10 ** 3}
|
units = {"p": 10**12, "n": 10**9, "u": 10**6, "m": 10**3}
|
||||||
unit = str(amount)[-1]
|
unit = str(amount)[-1]
|
||||||
|
|
||||||
# BOLT #11:
|
# BOLT #11:
|
||||||
@ -348,9 +348,9 @@ def _trim_to_bytes(barr):
|
|||||||
|
|
||||||
def _readable_scid(short_channel_id: int) -> str:
|
def _readable_scid(short_channel_id: int) -> str:
|
||||||
return "{blockheight}x{transactionindex}x{outputindex}".format(
|
return "{blockheight}x{transactionindex}x{outputindex}".format(
|
||||||
blockheight=((short_channel_id >> 40) & 0xffffff),
|
blockheight=((short_channel_id >> 40) & 0xFFFFFF),
|
||||||
transactionindex=((short_channel_id >> 16) & 0xffffff),
|
transactionindex=((short_channel_id >> 16) & 0xFFFFFF),
|
||||||
outputindex=(short_channel_id & 0xffff),
|
outputindex=(short_channel_id & 0xFFFF),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -391,7 +391,11 @@ async def api_payment(payment_hash, X_Api_Key: Optional[str] = Header(None)):
|
|||||||
return {"paid": False}
|
return {"paid": False}
|
||||||
|
|
||||||
if wallet and wallet.id == payment.wallet_id:
|
if wallet and wallet.id == payment.wallet_id:
|
||||||
return {"paid": not payment.pending, "preimage": payment.preimage, "details": payment}
|
return {
|
||||||
|
"paid": not payment.pending,
|
||||||
|
"preimage": payment.preimage,
|
||||||
|
"details": payment,
|
||||||
|
}
|
||||||
return {"paid": not payment.pending, "preimage": payment.preimage}
|
return {"paid": not payment.pending, "preimage": payment.preimage}
|
||||||
|
|
||||||
|
|
||||||
|
@ -226,7 +226,9 @@ async def lnurl_balance_notify(request: Request, service: str):
|
|||||||
redeem_lnurl_withdraw(bc.wallet, bc.url)
|
redeem_lnurl_withdraw(bc.wallet, bc.url)
|
||||||
|
|
||||||
|
|
||||||
@core_html_routes.get("/lnurlwallet", response_class=RedirectResponse, name="core.lnurlwallet")
|
@core_html_routes.get(
|
||||||
|
"/lnurlwallet", response_class=RedirectResponse, name="core.lnurlwallet"
|
||||||
|
)
|
||||||
async def lnurlwallet(request: Request):
|
async def lnurlwallet(request: Request):
|
||||||
async with db.connect() as conn:
|
async with db.connect() as conn:
|
||||||
account = await create_account(conn=conn)
|
account = await create_account(conn=conn)
|
||||||
|
@ -13,7 +13,11 @@ from starlette.requests import Request
|
|||||||
from lnbits.core.crud import get_user, get_wallet_for_key
|
from lnbits.core.crud import get_user, get_wallet_for_key
|
||||||
from lnbits.core.models import User, Wallet
|
from lnbits.core.models import User, Wallet
|
||||||
from lnbits.requestvars import g
|
from lnbits.requestvars import g
|
||||||
from lnbits.settings import LNBITS_ALLOWED_USERS, LNBITS_ADMIN_USERS, LNBITS_ADMIN_EXTENSIONS
|
from lnbits.settings import (
|
||||||
|
LNBITS_ALLOWED_USERS,
|
||||||
|
LNBITS_ADMIN_USERS,
|
||||||
|
LNBITS_ADMIN_EXTENSIONS,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class KeyChecker(SecurityBase):
|
class KeyChecker(SecurityBase):
|
||||||
@ -122,7 +126,7 @@ async def get_key_type(
|
|||||||
# 0: admin
|
# 0: admin
|
||||||
# 1: invoice
|
# 1: invoice
|
||||||
# 2: invalid
|
# 2: invalid
|
||||||
pathname = r['path'].split('/')[1]
|
pathname = r["path"].split("/")[1]
|
||||||
|
|
||||||
if not api_key_header and not api_key_query:
|
if not api_key_header and not api_key_query:
|
||||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST)
|
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST)
|
||||||
@ -133,8 +137,12 @@ async def get_key_type(
|
|||||||
checker = WalletAdminKeyChecker(api_key=token)
|
checker = WalletAdminKeyChecker(api_key=token)
|
||||||
await checker.__call__(r)
|
await checker.__call__(r)
|
||||||
wallet = WalletTypeInfo(0, checker.wallet)
|
wallet = WalletTypeInfo(0, checker.wallet)
|
||||||
if (LNBITS_ADMIN_USERS and wallet.wallet.user not in LNBITS_ADMIN_USERS) and (LNBITS_ADMIN_EXTENSIONS and pathname in LNBITS_ADMIN_EXTENSIONS):
|
if (LNBITS_ADMIN_USERS and wallet.wallet.user not in LNBITS_ADMIN_USERS) and (
|
||||||
raise HTTPException(status_code=HTTPStatus.UNAUTHORIZED, detail="User not authorized.")
|
LNBITS_ADMIN_EXTENSIONS and pathname in LNBITS_ADMIN_EXTENSIONS
|
||||||
|
):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.UNAUTHORIZED, detail="User not authorized."
|
||||||
|
)
|
||||||
return wallet
|
return wallet
|
||||||
except HTTPException as e:
|
except HTTPException as e:
|
||||||
if e.status_code == HTTPStatus.BAD_REQUEST:
|
if e.status_code == HTTPStatus.BAD_REQUEST:
|
||||||
@ -148,8 +156,12 @@ async def get_key_type(
|
|||||||
checker = WalletInvoiceKeyChecker(api_key=token)
|
checker = WalletInvoiceKeyChecker(api_key=token)
|
||||||
await checker.__call__(r)
|
await checker.__call__(r)
|
||||||
wallet = WalletTypeInfo(1, checker.wallet)
|
wallet = WalletTypeInfo(1, checker.wallet)
|
||||||
if (LNBITS_ADMIN_USERS and wallet.wallet.user not in LNBITS_ADMIN_USERS) and (LNBITS_ADMIN_EXTENSIONS and pathname in LNBITS_ADMIN_EXTENSIONS):
|
if (LNBITS_ADMIN_USERS and wallet.wallet.user not in LNBITS_ADMIN_USERS) and (
|
||||||
raise HTTPException(status_code=HTTPStatus.UNAUTHORIZED, detail="User not authorized.")
|
LNBITS_ADMIN_EXTENSIONS and pathname in LNBITS_ADMIN_EXTENSIONS
|
||||||
|
):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.UNAUTHORIZED, detail="User not authorized."
|
||||||
|
)
|
||||||
return wallet
|
return wallet
|
||||||
except HTTPException as e:
|
except HTTPException as e:
|
||||||
if e.status_code == HTTPStatus.BAD_REQUEST:
|
if e.status_code == HTTPStatus.BAD_REQUEST:
|
||||||
|
@ -11,6 +11,7 @@ class CreateUserData(BaseModel):
|
|||||||
admin_id: str = Query(...)
|
admin_id: str = Query(...)
|
||||||
discord_id: str = Query("")
|
discord_id: str = Query("")
|
||||||
|
|
||||||
|
|
||||||
class CreateUserWallet(BaseModel):
|
class CreateUserWallet(BaseModel):
|
||||||
user_id: str = Query(...)
|
user_id: str = Query(...)
|
||||||
wallet_name: str = Query(...)
|
wallet_name: str = Query(...)
|
||||||
@ -23,6 +24,7 @@ class Users(BaseModel):
|
|||||||
admin: str
|
admin: str
|
||||||
discord_id: str
|
discord_id: str
|
||||||
|
|
||||||
|
|
||||||
class Wallets(BaseModel):
|
class Wallets(BaseModel):
|
||||||
id: str
|
id: str
|
||||||
admin: str
|
admin: str
|
||||||
|
@ -109,9 +109,7 @@ async def api_discordbot_wallet_transactions(
|
|||||||
async def api_discordbot_users_wallets(
|
async def api_discordbot_users_wallets(
|
||||||
user_id, wallet: WalletTypeInfo = Depends(get_key_type)
|
user_id, wallet: WalletTypeInfo = Depends(get_key_type)
|
||||||
):
|
):
|
||||||
return [
|
return [s_wallet.dict() for s_wallet in await get_discordbot_users_wallets(user_id)]
|
||||||
s_wallet.dict() for s_wallet in await get_discordbot_users_wallets(user_id)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
@discordbot_ext.delete("/api/v1/wallets/{wallet_id}")
|
@discordbot_ext.delete("/api/v1/wallets/{wallet_id}")
|
||||||
|
@ -5,10 +5,7 @@ from lnbits.helpers import template_renderer
|
|||||||
|
|
||||||
db = Database("ext_example")
|
db = Database("ext_example")
|
||||||
|
|
||||||
example_ext: APIRouter = APIRouter(
|
example_ext: APIRouter = APIRouter(prefix="/example", tags=["example"])
|
||||||
prefix="/example",
|
|
||||||
tags=["example"]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def example_renderer():
|
def example_renderer():
|
||||||
|
@ -3,4 +3,3 @@
|
|||||||
# class Example(BaseModel):
|
# class Example(BaseModel):
|
||||||
# id: str
|
# id: str
|
||||||
# wallet: str
|
# wallet: str
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ from . import example_ext
|
|||||||
|
|
||||||
# add your endpoints here
|
# add your endpoints here
|
||||||
|
|
||||||
|
|
||||||
@example_ext.get("/api/v1/tools")
|
@example_ext.get("/api/v1/tools")
|
||||||
async def api_example():
|
async def api_example():
|
||||||
"""Try to add descriptions for others."""
|
"""Try to add descriptions for others."""
|
||||||
|
@ -455,5 +455,6 @@ async def api_get_jukebox_currently(
|
|||||||
)
|
)
|
||||||
except:
|
except:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.NOT_FOUND, detail="Something went wrong, or no song is playing yet"
|
status_code=HTTPStatus.NOT_FOUND,
|
||||||
|
detail="Something went wrong, or no song is playing yet",
|
||||||
)
|
)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
|
|
||||||
# from mmap import MAP_DENYWRITE
|
# from mmap import MAP_DENYWRITE
|
||||||
|
|
||||||
from fastapi.param_functions import Depends
|
from fastapi.param_functions import Depends
|
||||||
|
@ -8,8 +8,8 @@ def hotp(key, counter, digits=6, digest="sha1"):
|
|||||||
key = base64.b32decode(key.upper() + "=" * ((8 - len(key)) % 8))
|
key = base64.b32decode(key.upper() + "=" * ((8 - len(key)) % 8))
|
||||||
counter = struct.pack(">Q", counter)
|
counter = struct.pack(">Q", counter)
|
||||||
mac = hmac.new(key, counter, digest).digest()
|
mac = hmac.new(key, counter, digest).digest()
|
||||||
offset = mac[-1] & 0x0f
|
offset = mac[-1] & 0x0F
|
||||||
binary = struct.unpack(">L", mac[offset : offset + 4])[0] & 0x7fffffff
|
binary = struct.unpack(">L", mac[offset : offset + 4])[0] & 0x7FFFFFFF
|
||||||
return str(binary)[-digits:].zfill(digits)
|
return str(binary)[-digits:].zfill(digits)
|
||||||
|
|
||||||
|
|
||||||
|
@ -54,8 +54,7 @@ async def api_paywall_delete(
|
|||||||
|
|
||||||
@paywall_ext.post("/api/v1/paywalls/invoice/{paywall_id}")
|
@paywall_ext.post("/api/v1/paywalls/invoice/{paywall_id}")
|
||||||
async def api_paywall_create_invoice(
|
async def api_paywall_create_invoice(
|
||||||
data: CreatePaywallInvoice,
|
data: CreatePaywallInvoice, paywall_id: str = Query(None)
|
||||||
paywall_id: str = Query(None)
|
|
||||||
):
|
):
|
||||||
paywall = await get_paywall(paywall_id)
|
paywall = await get_paywall(paywall_id)
|
||||||
if data.amount < paywall.amount:
|
if data.amount < paywall.amount:
|
||||||
@ -78,7 +77,9 @@ async def api_paywall_create_invoice(
|
|||||||
|
|
||||||
|
|
||||||
@paywall_ext.post("/api/v1/paywalls/check_invoice/{paywall_id}")
|
@paywall_ext.post("/api/v1/paywalls/check_invoice/{paywall_id}")
|
||||||
async def api_paywal_check_invoice(data: CheckPaywallInvoice, paywall_id: str = Query(None)):
|
async def api_paywal_check_invoice(
|
||||||
|
data: CheckPaywallInvoice, paywall_id: str = Query(None)
|
||||||
|
):
|
||||||
paywall = await get_paywall(paywall_id)
|
paywall = await get_paywall(paywall_id)
|
||||||
payment_hash = data.payment_hash
|
payment_hash = data.payment_hash
|
||||||
if not paywall:
|
if not paywall:
|
||||||
|
@ -123,7 +123,7 @@ async def api_create_donation(data: CreateDonation, request: Request):
|
|||||||
completelinktext="Back to Stream!",
|
completelinktext="Back to Stream!",
|
||||||
webhook=webhook_base + "/streamalerts/api/v1/postdonation",
|
webhook=webhook_base + "/streamalerts/api/v1/postdonation",
|
||||||
description=description,
|
description=description,
|
||||||
**charge_details
|
**charge_details,
|
||||||
)
|
)
|
||||||
charge = await create_charge(user=charge_details["user"], data=create_charge_data)
|
charge = await create_charge(user=charge_details["user"], data=create_charge_data)
|
||||||
await create_donation(
|
await create_donation(
|
||||||
|
@ -30,7 +30,9 @@ async def api_lnurl_response(request: Request, unique_hash):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if link.is_spent:
|
if link.is_spent:
|
||||||
raise HTTPException(status_code=HTTPStatus.NOT_FOUND, detail="Withdraw is spent.")
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.NOT_FOUND, detail="Withdraw is spent."
|
||||||
|
)
|
||||||
url = request.url_for("withdraw.api_lnurl_callback", unique_hash=link.unique_hash)
|
url = request.url_for("withdraw.api_lnurl_callback", unique_hash=link.unique_hash)
|
||||||
withdrawResponse = {
|
withdrawResponse = {
|
||||||
"tag": "withdrawRequest",
|
"tag": "withdrawRequest",
|
||||||
@ -48,7 +50,11 @@ async def api_lnurl_response(request: Request, unique_hash):
|
|||||||
|
|
||||||
@withdraw_ext.get("/api/v1/lnurl/cb/{unique_hash}", name="withdraw.api_lnurl_callback")
|
@withdraw_ext.get("/api/v1/lnurl/cb/{unique_hash}", name="withdraw.api_lnurl_callback")
|
||||||
async def api_lnurl_callback(
|
async def api_lnurl_callback(
|
||||||
unique_hash, request: Request, k1: str = Query(...), pr: str = Query(...), id_unique_hash=None
|
unique_hash,
|
||||||
|
request: Request,
|
||||||
|
k1: str = Query(...),
|
||||||
|
pr: str = Query(...),
|
||||||
|
id_unique_hash=None,
|
||||||
):
|
):
|
||||||
link = await get_withdraw_link_by_hash(unique_hash)
|
link = await get_withdraw_link_by_hash(unique_hash)
|
||||||
now = int(datetime.now().timestamp())
|
now = int(datetime.now().timestamp())
|
||||||
@ -58,7 +64,9 @@ async def api_lnurl_callback(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if link.is_spent:
|
if link.is_spent:
|
||||||
raise HTTPException(status_code=HTTPStatus.NOT_FOUND, detail="Withdraw is spent.")
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.NOT_FOUND, detail="Withdraw is spent."
|
||||||
|
)
|
||||||
|
|
||||||
if link.k1 != k1:
|
if link.k1 != k1:
|
||||||
raise HTTPException(status_code=HTTPStatus.NOT_FOUND, detail="Bad request.")
|
raise HTTPException(status_code=HTTPStatus.NOT_FOUND, detail="Bad request.")
|
||||||
@ -81,7 +89,7 @@ async def api_lnurl_callback(
|
|||||||
if id_unique_hash == shortuuid.uuid(name=tohash):
|
if id_unique_hash == shortuuid.uuid(name=tohash):
|
||||||
found = True
|
found = True
|
||||||
useslist.pop(ind)
|
useslist.pop(ind)
|
||||||
usescsv = ','.join(useslist)
|
usescsv = ",".join(useslist)
|
||||||
if not found:
|
if not found:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.NOT_FOUND, detail="LNURL-withdraw not found."
|
status_code=HTTPStatus.NOT_FOUND, detail="LNURL-withdraw not found."
|
||||||
@ -134,7 +142,9 @@ async def api_lnurl_multi_response(request: Request, unique_hash, id_unique_hash
|
|||||||
)
|
)
|
||||||
|
|
||||||
if link.is_spent:
|
if link.is_spent:
|
||||||
raise HTTPException(status_code=HTTPStatus.NOT_FOUND, detail="Withdraw is spent.")
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.NOT_FOUND, detail="Withdraw is spent."
|
||||||
|
)
|
||||||
|
|
||||||
useslist = link.usescsv.split(",")
|
useslist = link.usescsv.split(",")
|
||||||
found = False
|
found = False
|
||||||
|
@ -103,6 +103,7 @@ async def print_qr(request: Request, link_id):
|
|||||||
"withdraw/print_qr.html", {"request": request, "link": linked, "unique": True}
|
"withdraw/print_qr.html", {"request": request, "link": linked, "unique": True}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@withdraw_ext.get("/csv/{link_id}", response_class=HTMLResponse)
|
@withdraw_ext.get("/csv/{link_id}", response_class=HTMLResponse)
|
||||||
async def print_qr(request: Request, link_id):
|
async def print_qr(request: Request, link_id):
|
||||||
link = await get_withdraw_link(link_id)
|
link = await get_withdraw_link(link_id)
|
||||||
|
@ -72,9 +72,7 @@ async def api_link_create_or_update(
|
|||||||
wallet: WalletTypeInfo = Depends(require_admin_key),
|
wallet: WalletTypeInfo = Depends(require_admin_key),
|
||||||
):
|
):
|
||||||
if data.uses > 250:
|
if data.uses > 250:
|
||||||
raise HTTPException(
|
raise HTTPException(detail="250 uses max.", status_code=HTTPStatus.BAD_REQUEST)
|
||||||
detail="250 uses max.", status_code=HTTPStatus.BAD_REQUEST
|
|
||||||
)
|
|
||||||
|
|
||||||
if data.min_withdrawable < 1:
|
if data.min_withdrawable < 1:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
|
@ -26,7 +26,9 @@ class Extension(NamedTuple):
|
|||||||
class ExtensionManager:
|
class ExtensionManager:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._disabled: List[str] = settings.LNBITS_DISABLED_EXTENSIONS
|
self._disabled: List[str] = settings.LNBITS_DISABLED_EXTENSIONS
|
||||||
self._admin_only: List[str] = [x.strip(' ') for x in settings.LNBITS_ADMIN_EXTENSIONS]
|
self._admin_only: List[str] = [
|
||||||
|
x.strip(" ") for x in settings.LNBITS_ADMIN_EXTENSIONS
|
||||||
|
]
|
||||||
self._extension_folders: List[str] = [
|
self._extension_folders: List[str] = [
|
||||||
x[1] for x in os.walk(os.path.join(settings.LNBITS_PATH, "extensions"))
|
x[1] for x in os.walk(os.path.join(settings.LNBITS_PATH, "extensions"))
|
||||||
][0]
|
][0]
|
||||||
|
@ -29,7 +29,9 @@ LNBITS_ALLOWED_USERS: List[str] = env.list(
|
|||||||
"LNBITS_ALLOWED_USERS", default=[], subcast=str
|
"LNBITS_ALLOWED_USERS", default=[], subcast=str
|
||||||
)
|
)
|
||||||
LNBITS_ADMIN_USERS: List[str] = env.list("LNBITS_ADMIN_USERS", default=[], subcast=str)
|
LNBITS_ADMIN_USERS: List[str] = env.list("LNBITS_ADMIN_USERS", default=[], subcast=str)
|
||||||
LNBITS_ADMIN_EXTENSIONS: List[str] = env.list("LNBITS_ADMIN_EXTENSIONS", default=[], subcast=str)
|
LNBITS_ADMIN_EXTENSIONS: List[str] = env.list(
|
||||||
|
"LNBITS_ADMIN_EXTENSIONS", default=[], subcast=str
|
||||||
|
)
|
||||||
LNBITS_DISABLED_EXTENSIONS: List[str] = env.list(
|
LNBITS_DISABLED_EXTENSIONS: List[str] = env.list(
|
||||||
"LNBITS_DISABLED_EXTENSIONS", default=[], subcast=str
|
"LNBITS_DISABLED_EXTENSIONS", default=[], subcast=str
|
||||||
)
|
)
|
||||||
|
@ -29,6 +29,7 @@ class EclairError(Exception):
|
|||||||
class UnknownError(Exception):
|
class UnknownError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class EclairWallet(Wallet):
|
class EclairWallet(Wallet):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
url = getenv("ECLAIR_URL")
|
url = getenv("ECLAIR_URL")
|
||||||
@ -41,13 +42,10 @@ class EclairWallet(Wallet):
|
|||||||
auth = str(encodedAuth, "utf-8")
|
auth = str(encodedAuth, "utf-8")
|
||||||
self.auth = {"Authorization": f"Basic {auth}"}
|
self.auth = {"Authorization": f"Basic {auth}"}
|
||||||
|
|
||||||
|
|
||||||
async def status(self) -> StatusResponse:
|
async def status(self) -> StatusResponse:
|
||||||
async with httpx.AsyncClient() as client:
|
async with httpx.AsyncClient() as client:
|
||||||
r = await client.post(
|
r = await client.post(
|
||||||
f"{self.url}/usablebalances",
|
f"{self.url}/usablebalances", headers=self.auth, timeout=40
|
||||||
headers=self.auth,
|
|
||||||
timeout=40
|
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
data = r.json()
|
data = r.json()
|
||||||
@ -76,10 +74,7 @@ class EclairWallet(Wallet):
|
|||||||
|
|
||||||
async with httpx.AsyncClient() as client:
|
async with httpx.AsyncClient() as client:
|
||||||
r = await client.post(
|
r = await client.post(
|
||||||
f"{self.url}/createinvoice",
|
f"{self.url}/createinvoice", headers=self.auth, data=data, timeout=40
|
||||||
headers=self.auth,
|
|
||||||
data=data,
|
|
||||||
timeout=40
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if r.is_error:
|
if r.is_error:
|
||||||
@ -95,7 +90,6 @@ class EclairWallet(Wallet):
|
|||||||
data = r.json()
|
data = r.json()
|
||||||
return InvoiceResponse(True, data["paymentHash"], data["serialized"], None)
|
return InvoiceResponse(True, data["paymentHash"], data["serialized"], None)
|
||||||
|
|
||||||
|
|
||||||
async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse:
|
async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse:
|
||||||
async with httpx.AsyncClient() as client:
|
async with httpx.AsyncClient() as client:
|
||||||
r = await client.post(
|
r = await client.post(
|
||||||
@ -116,7 +110,6 @@ class EclairWallet(Wallet):
|
|||||||
|
|
||||||
data = r.json()
|
data = r.json()
|
||||||
|
|
||||||
|
|
||||||
checking_id = data["paymentHash"]
|
checking_id = data["paymentHash"]
|
||||||
preimage = data["paymentPreimage"]
|
preimage = data["paymentPreimage"]
|
||||||
|
|
||||||
@ -135,7 +128,9 @@ class EclairWallet(Wallet):
|
|||||||
except:
|
except:
|
||||||
error_message = r.text
|
error_message = r.text
|
||||||
pass
|
pass
|
||||||
return PaymentResponse(True, checking_id, 0, preimage, error_message) ## ?? is this ok ??
|
return PaymentResponse(
|
||||||
|
True, checking_id, 0, preimage, error_message
|
||||||
|
) ## ?? is this ok ??
|
||||||
|
|
||||||
data = r.json()
|
data = r.json()
|
||||||
fees = [i["status"] for i in data]
|
fees = [i["status"] for i in data]
|
||||||
@ -143,14 +138,12 @@ class EclairWallet(Wallet):
|
|||||||
|
|
||||||
return PaymentResponse(True, checking_id, fee_msat, preimage, None)
|
return PaymentResponse(True, checking_id, fee_msat, preimage, None)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async def get_invoice_status(self, checking_id: str) -> PaymentStatus:
|
async def get_invoice_status(self, checking_id: str) -> PaymentStatus:
|
||||||
async with httpx.AsyncClient() as client:
|
async with httpx.AsyncClient() as client:
|
||||||
r = await client.post(
|
r = await client.post(
|
||||||
f"{self.url}/getreceivedinfo",
|
f"{self.url}/getreceivedinfo",
|
||||||
headers=self.auth,
|
headers=self.auth,
|
||||||
data={"paymentHash": checking_id}
|
data={"paymentHash": checking_id},
|
||||||
)
|
)
|
||||||
data = r.json()
|
data = r.json()
|
||||||
|
|
||||||
@ -167,8 +160,7 @@ class EclairWallet(Wallet):
|
|||||||
r = await client.post(
|
r = await client.post(
|
||||||
url=f"{self.url}/getsentinfo",
|
url=f"{self.url}/getsentinfo",
|
||||||
headers=self.auth,
|
headers=self.auth,
|
||||||
data={"paymentHash": checking_id}
|
data={"paymentHash": checking_id},
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
data = r.json()[0]
|
data = r.json()[0]
|
||||||
@ -184,7 +176,10 @@ class EclairWallet(Wallet):
|
|||||||
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
|
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
async with connect(self.ws_url, extra_headers=[('Authorization', self.auth["Authorization"])]) as ws:
|
async with connect(
|
||||||
|
self.ws_url,
|
||||||
|
extra_headers=[("Authorization", self.auth["Authorization"])],
|
||||||
|
) as ws:
|
||||||
while True:
|
while True:
|
||||||
message = await ws.recv()
|
message = await ws.recv()
|
||||||
message = json.loads(message)
|
message = json.loads(message)
|
||||||
@ -192,8 +187,13 @@ class EclairWallet(Wallet):
|
|||||||
if message and message["type"] == "payment-received":
|
if message and message["type"] == "payment-received":
|
||||||
yield message["paymentHash"]
|
yield message["paymentHash"]
|
||||||
|
|
||||||
except (OSError, ConnectionClosedOK, ConnectionClosedError, ConnectionClosed) as ose:
|
except (
|
||||||
print('OSE', ose)
|
OSError,
|
||||||
|
ConnectionClosedOK,
|
||||||
|
ConnectionClosedError,
|
||||||
|
ConnectionClosed,
|
||||||
|
) as ose:
|
||||||
|
print("OSE", ose)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
print("lost connection to eclair's websocket, retrying in 5 seconds")
|
print("lost connection to eclair's websocket, retrying in 5 seconds")
|
||||||
|
@ -7,6 +7,7 @@ import getpass
|
|||||||
BLOCK_SIZE = 16
|
BLOCK_SIZE = 16
|
||||||
import getpass
|
import getpass
|
||||||
|
|
||||||
|
|
||||||
def load_macaroon(macaroon: str) -> str:
|
def load_macaroon(macaroon: str) -> str:
|
||||||
"""Returns hex version of a macaroon encoded in base64 or the file path.
|
"""Returns hex version of a macaroon encoded in base64 or the file path.
|
||||||
|
|
||||||
@ -29,6 +30,7 @@ def load_macaroon(macaroon: str) -> str:
|
|||||||
pass
|
pass
|
||||||
return macaroon
|
return macaroon
|
||||||
|
|
||||||
|
|
||||||
class AESCipher(object):
|
class AESCipher(object):
|
||||||
"""This class is compatible with crypto-js/aes.js
|
"""This class is compatible with crypto-js/aes.js
|
||||||
|
|
||||||
@ -39,6 +41,7 @@ class AESCipher(object):
|
|||||||
AES.decrypt(encrypted, password).toString(Utf8);
|
AES.decrypt(encrypted, password).toString(Utf8);
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, key=None, description=""):
|
def __init__(self, key=None, description=""):
|
||||||
self.key = key
|
self.key = key
|
||||||
self.description = description + " "
|
self.description = description + " "
|
||||||
@ -47,7 +50,6 @@ class AESCipher(object):
|
|||||||
length = BLOCK_SIZE - (len(data) % BLOCK_SIZE)
|
length = BLOCK_SIZE - (len(data) % BLOCK_SIZE)
|
||||||
return data + (chr(length) * length).encode()
|
return data + (chr(length) * length).encode()
|
||||||
|
|
||||||
|
|
||||||
def unpad(self, data):
|
def unpad(self, data):
|
||||||
return data[: -(data[-1] if type(data[-1]) == int else ord(data[-1]))]
|
return data[: -(data[-1] if type(data[-1]) == int else ord(data[-1]))]
|
||||||
|
|
||||||
@ -70,8 +72,7 @@ class AESCipher(object):
|
|||||||
return final_key[:output]
|
return final_key[:output]
|
||||||
|
|
||||||
def decrypt(self, encrypted: str) -> str:
|
def decrypt(self, encrypted: str) -> str:
|
||||||
"""Decrypts a string using AES-256-CBC.
|
"""Decrypts a string using AES-256-CBC."""
|
||||||
"""
|
|
||||||
passphrase = self.passphrase
|
passphrase = self.passphrase
|
||||||
encrypted = base64.b64decode(encrypted)
|
encrypted = base64.b64decode(encrypted)
|
||||||
assert encrypted[0:8] == b"Salted__"
|
assert encrypted[0:8] == b"Salted__"
|
||||||
@ -92,7 +93,10 @@ class AESCipher(object):
|
|||||||
key = key_iv[:32]
|
key = key_iv[:32]
|
||||||
iv = key_iv[32:]
|
iv = key_iv[32:]
|
||||||
aes = AES.new(key, AES.MODE_CBC, iv)
|
aes = AES.new(key, AES.MODE_CBC, iv)
|
||||||
return base64.b64encode(b"Salted__" + salt + aes.encrypt(self.pad(message))).decode()
|
return base64.b64encode(
|
||||||
|
b"Salted__" + salt + aes.encrypt(self.pad(message))
|
||||||
|
).decode()
|
||||||
|
|
||||||
|
|
||||||
# if this file is executed directly, ask for a macaroon and encrypt it
|
# if this file is executed directly, ask for a macaroon and encrypt it
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
@ -19,9 +19,10 @@ def app():
|
|||||||
loop.run_until_complete(loop.shutdown_asyncgens())
|
loop.run_until_complete(loop.shutdown_asyncgens())
|
||||||
loop.close()
|
loop.close()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
async def client(app):
|
async def client(app):
|
||||||
client = AsyncClient(app=app, base_url=f'http://{HOST}:{PORT}')
|
client = AsyncClient(app=app, base_url=f"http://{HOST}:{PORT}")
|
||||||
# yield and pass the client to the test
|
# yield and pass the client to the test
|
||||||
yield client
|
yield client
|
||||||
# close the async client after the test has finished
|
# close the async client after the test has finished
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import pytest
|
import pytest
|
||||||
from tests.conftest import client
|
from tests.conftest import client
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_core_views_generic(client):
|
async def test_core_views_generic(client):
|
||||||
response = await client.get("/")
|
response = await client.get("/")
|
||||||
|
@ -4,16 +4,22 @@ import secrets
|
|||||||
from lnbits.core.crud import create_account, create_wallet
|
from lnbits.core.crud import create_account, create_wallet
|
||||||
from lnbits.extensions.bleskomat.crud import create_bleskomat, create_bleskomat_lnurl
|
from lnbits.extensions.bleskomat.crud import create_bleskomat, create_bleskomat_lnurl
|
||||||
from lnbits.extensions.bleskomat.models import CreateBleskomat
|
from lnbits.extensions.bleskomat.models import CreateBleskomat
|
||||||
from lnbits.extensions.bleskomat.helpers import generate_bleskomat_lnurl_secret, generate_bleskomat_lnurl_signature, prepare_lnurl_params, query_to_signing_payload
|
from lnbits.extensions.bleskomat.helpers import (
|
||||||
|
generate_bleskomat_lnurl_secret,
|
||||||
|
generate_bleskomat_lnurl_signature,
|
||||||
|
prepare_lnurl_params,
|
||||||
|
query_to_signing_payload,
|
||||||
|
)
|
||||||
from lnbits.extensions.bleskomat.exchange_rates import exchange_rate_providers
|
from lnbits.extensions.bleskomat.exchange_rates import exchange_rate_providers
|
||||||
|
|
||||||
exchange_rate_providers["dummy"] = {
|
exchange_rate_providers["dummy"] = {
|
||||||
"name": "dummy",
|
"name": "dummy",
|
||||||
"domain": None,
|
"domain": None,
|
||||||
"api_url": None,
|
"api_url": None,
|
||||||
"getter": lambda data, replacements: str(1e8),# 1 BTC = 100000000 sats
|
"getter": lambda data, replacements: str(1e8), # 1 BTC = 100000000 sats
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
async def bleskomat():
|
async def bleskomat():
|
||||||
user = await create_account()
|
user = await create_account()
|
||||||
@ -22,11 +28,12 @@ async def bleskomat():
|
|||||||
name="Test Bleskomat",
|
name="Test Bleskomat",
|
||||||
fiat_currency="EUR",
|
fiat_currency="EUR",
|
||||||
exchange_rate_provider="dummy",
|
exchange_rate_provider="dummy",
|
||||||
fee="0"
|
fee="0",
|
||||||
)
|
)
|
||||||
bleskomat = await create_bleskomat(data=data, wallet_id=wallet.id)
|
bleskomat = await create_bleskomat(data=data, wallet_id=wallet.id)
|
||||||
return bleskomat
|
return bleskomat
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
async def lnurl(bleskomat):
|
async def lnurl(bleskomat):
|
||||||
query = {
|
query = {
|
||||||
@ -43,7 +50,7 @@ async def lnurl(bleskomat):
|
|||||||
signature = generate_bleskomat_lnurl_signature(
|
signature = generate_bleskomat_lnurl_signature(
|
||||||
payload=payload,
|
payload=payload,
|
||||||
api_key_secret=bleskomat.api_key_secret,
|
api_key_secret=bleskomat.api_key_secret,
|
||||||
api_key_encoding=bleskomat.api_key_encoding
|
api_key_encoding=bleskomat.api_key_encoding,
|
||||||
)
|
)
|
||||||
secret = generate_bleskomat_lnurl_secret(bleskomat.api_key_id, signature)
|
secret = generate_bleskomat_lnurl_secret(bleskomat.api_key_id, signature)
|
||||||
params = json.JSONEncoder().encode(params)
|
params = json.JSONEncoder().encode(params)
|
||||||
|
@ -2,6 +2,7 @@ import hashlib
|
|||||||
import secrets
|
import secrets
|
||||||
from lnbits.core.crud import create_payment
|
from lnbits.core.crud import create_payment
|
||||||
|
|
||||||
|
|
||||||
async def credit_wallet(wallet_id: str, amount: int):
|
async def credit_wallet(wallet_id: str, amount: int):
|
||||||
preimage = secrets.token_hex(32)
|
preimage = secrets.token_hex(32)
|
||||||
m = hashlib.sha256()
|
m = hashlib.sha256()
|
||||||
@ -14,6 +15,6 @@ async def credit_wallet(wallet_id: str, amount: int):
|
|||||||
checking_id=payment_hash,
|
checking_id=payment_hash,
|
||||||
preimage=preimage,
|
preimage=preimage,
|
||||||
memo="",
|
memo="",
|
||||||
amount=amount,# msat
|
amount=amount, # msat
|
||||||
pending=False,# not pending, so it will increase the wallet's balance
|
pending=False, # not pending, so it will increase the wallet's balance
|
||||||
)
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user