refactor: clean public functions

This commit is contained in:
Vlad Stan 2026-07-30 11:51:12 +03:00
parent e2329b1453
commit dbcf2cce59
3 changed files with 119 additions and 138 deletions

View file

@ -4,15 +4,13 @@ from uuid import uuid4
from lnbits.core.db import db
from lnbits.core.models.wallets import BaseWallet, WalletsFilters, WalletType
from lnbits.db import Connection, Database, Filters, Page
from lnbits.db import Connection, Filters, Page
from lnbits.helpers import generate_ln_address
from lnbits.settings import settings
from lnbits.utils.cache import cache
from ..models import Wallet
_LEGACY_LNURLP_DB = Database("ext_lnurlp")
async def create_wallet(
*,

View file

@ -16,10 +16,15 @@ from pydantic import parse_obj_as
from lnbits.core.crud.wallets import (
get_wallet,
get_wallet_id_by_ln_address,
legacy_lnurlp_address_exists,
update_wallet,
)
from lnbits.core.db import db
from lnbits.core.models.payments import CreateInvoice
from lnbits.core.models.wallets import Wallet
from lnbits.core.services.payments import (
create_invoice,
create_wallet_invoice,
pay_invoice,
)
from lnbits.db import Connection
from lnbits.exceptions import PaymentError
from lnbits.settings import settings
@ -29,100 +34,6 @@ COMMENT_ALLOWED = 799
LIGHTNING_ADDRESS_REGEX = re.compile(r"^[a-z0-9_.-]{1,210}$")
def normalize_lightning_address_local_part(local_part: str) -> str:
return local_part.strip().lower()
def _blacklist_words() -> set[str]:
return {
word.strip().lower()
for word in settings.lnbits_wallet_lightning_address_blacklist
if word.strip()
}
def _uses_blacklisted_word(local_part: str) -> bool:
words = _blacklist_words()
if not words:
return False
segments = [segment for segment in re.split(r"[._-]+", local_part) if segment]
return local_part in words or any(segment in words for segment in segments)
async def validate_lightning_address_local_part(
*,
local_part: str,
wallet: Wallet,
allow_blacklisted: bool = False,
conn: Connection | None = None,
) -> str:
local_part = normalize_lightning_address_local_part(local_part)
if not local_part:
raise ValueError("Lightning Address is required.")
if "+" in local_part:
raise ValueError("Lightning Address cannot include tags.")
if "@" in local_part:
raise ValueError("Enter only the Lightning Address name before @.")
if not LIGHTNING_ADDRESS_REGEX.match(local_part):
raise ValueError(
"Lightning Address can only contain lowercase letters, numbers, "
"dash, underscore, and dot."
)
if not allow_blacklisted and _uses_blacklisted_word(local_part):
raise ValueError("Lightning Address contains a reserved word.")
existing_wallet_id = await get_wallet_id_by_ln_address(local_part)
if existing_wallet_id and existing_wallet_id != wallet.id:
raise ValueError("Lightning Address is already taken.")
if await legacy_lnurlp_address_exists(local_part):
raise ValueError("Lightning Address is already taken.")
return local_part
async def _charge_for_lightning_address(wallet: Wallet) -> None:
price_sats = settings.lnbits_wallet_lightning_address_price_sats
if not settings.lnbits_charge_wallet_lightning_addresses or price_sats <= 0:
return
if not settings.lnbits_service_fee_wallet:
raise ValueError("Lightning Address fee wallet is not configured.")
if settings.lnbits_service_fee_wallet == wallet.source_wallet_id:
raise ValueError("Lightning Address fee wallet cannot be the same wallet.")
from lnbits.core.crud.wallets import get_wallet
from lnbits.core.models.payments import CreateInvoice
from lnbits.core.services.payments import create_wallet_invoice, pay_invoice
fee_wallet = await get_wallet(settings.lnbits_service_fee_wallet)
if not fee_wallet:
raise ValueError("Lightning Address fee wallet is not configured.")
invoice = await create_wallet_invoice(
settings.lnbits_service_fee_wallet,
CreateInvoice(
out=False,
amount=price_sats,
memo="Lightning Address fee",
internal=True,
extra={
"tag": "wallet_lightning_address_fee",
"wallet": wallet.source_wallet_id,
},
),
)
try:
await pay_invoice(
wallet_id=wallet.source_wallet_id,
payment_request=invoice.bolt11,
description="Lightning Address fee",
tag="wallet_lightning_address_fee",
extra={
"tag": "wallet_lightning_address_fee",
"fee_wallet": settings.lnbits_service_fee_wallet,
},
)
except PaymentError as exc:
raise ValueError(exc.message) from exc
async def set_wallet_lightning_address(
*,
wallet: Wallet,
@ -131,17 +42,13 @@ async def set_wallet_lightning_address(
charge: bool = False,
conn: Connection | None = None,
) -> Wallet:
# todo: recheck
if not settings.ln_address_creation_allowed:
raise ValueError("Wallet Lightning Addresses are disabled.")
if not wallet.is_lightning_wallet or wallet.deleted:
raise ValueError("Lightning Address can only be set for active wallets.")
local_part = await validate_lightning_address_local_part(
local_part=local_part,
wallet=wallet,
allow_blacklisted=allow_blacklisted,
conn=conn,
local_part = await _validate_local_part(
local_part, wallet.id, allow_blacklisted, conn=conn
)
if wallet.lightning_address == local_part:
return wallet
@ -150,30 +57,7 @@ async def set_wallet_lightning_address(
await _charge_for_lightning_address(wallet)
wallet.lightning_address = local_part
await (conn or db).update("wallets", wallet)
return await get_wallet(wallet.id, conn=conn) or wallet
def lightning_address_for_request(request: Request, local_part: str) -> str:
return f"{local_part}@{request.url.netloc}"
def _split_tagged_local_part(local_part: str) -> tuple[str, str | None]:
username, separator, tag = local_part.partition("+")
if not separator or not tag:
return username.lower(), None
return username.lower(), tag
def _metadata(identifier: str, tag: str | None = None) -> list[list[str]]:
metadata = [
["text/plain", f"Payment to {identifier}"],
["text/identifier", identifier],
]
if tag:
metadata.append(["text/tag", tag])
return metadata
return await update_wallet(wallet, conn=conn)
async def wallet_lightning_address_response(
@ -192,7 +76,7 @@ async def wallet_lightning_address_response(
"lnurl.api_wallet_lightning_address_callback",
username=tagged_local_part,
)
identifier = lightning_address_for_request(request, tagged_local_part)
identifier = _lightning_address_for_request(request, tagged_local_part)
return LnurlPayResponse(
callback=parse_obj_as(CallbackUrl, str(callback)),
minSendable=MilliSatoshi(1000),
@ -231,7 +115,7 @@ async def wallet_lightning_address_callback(
tagged_local_part = local_part
if tag:
tagged_local_part = f"{tagged_local_part}+{tag}"
identifier = lightning_address_for_request(request, tagged_local_part)
identifier = _lightning_address_for_request(request, tagged_local_part)
extra = {
"tag": "wallet_lightning_address",
"lnaddress": identifier,
@ -241,8 +125,6 @@ async def wallet_lightning_address_callback(
if comment:
extra["comment"] = comment
from lnbits.core.services.payments import create_invoice
metadata = LnurlPayMetadata(json.dumps(_metadata(identifier, tag)))
payment = await create_invoice(
wallet_id=wallet_id,
@ -253,3 +135,108 @@ async def wallet_lightning_address_callback(
)
invoice = parse_obj_as(LightningInvoice, LightningInvoice(payment.bolt11))
return LnurlPayActionResponse(pr=invoice, disposable=False)
def _lightning_address_for_request(request: Request, local_part: str) -> str:
return f"{local_part}@{request.url.netloc}"
async def _validate_local_part(
local_part: str,
wallet_id: str,
allow_blacklisted: bool = False,
conn: Connection | None = None,
) -> str:
local_part = local_part.strip().lower()
if not local_part:
raise ValueError("Lightning Address is required.")
if "+" in local_part:
raise ValueError("Lightning Address cannot include tags.")
if "@" in local_part:
raise ValueError("Enter only the Lightning Address name before @.")
if not LIGHTNING_ADDRESS_REGEX.match(local_part):
raise ValueError(
"Lightning Address can only contain lowercase letters, numbers, "
"dash, underscore, and dot."
)
if not allow_blacklisted and _uses_blacklisted_word(local_part):
raise ValueError("Lightning Address contains a reserved word.")
existing_wallet_id = await get_wallet_id_by_ln_address(local_part, conn=conn)
if existing_wallet_id and existing_wallet_id != wallet_id:
raise ValueError("Lightning Address is already taken.")
return local_part
def _split_tagged_local_part(local_part: str) -> tuple[str, str | None]:
username, separator, tag = local_part.partition("+")
if not separator or not tag:
return username.lower(), None
return username.lower(), tag
def _metadata(identifier: str, tag: str | None = None) -> list[list[str]]:
metadata = [
["text/plain", f"Payment to {identifier}"],
["text/identifier", identifier],
]
if tag:
metadata.append(["text/tag", tag])
return metadata
async def _charge_for_lightning_address(wallet: Wallet) -> None:
price_sats = settings.lnbits_wallet_lightning_address_price_sats
if not settings.lnbits_charge_wallet_lightning_addresses or price_sats <= 0:
return
if not settings.lnbits_service_fee_wallet:
raise ValueError("Lightning Address fee wallet is not configured.")
if settings.lnbits_service_fee_wallet == wallet.source_wallet_id:
raise ValueError("Lightning Address fee wallet cannot be the same wallet.")
fee_wallet = await get_wallet(settings.lnbits_service_fee_wallet)
if not fee_wallet:
raise ValueError("Lightning Address fee wallet is not configured.")
invoice = await create_wallet_invoice(
settings.lnbits_service_fee_wallet,
CreateInvoice(
out=False,
amount=price_sats,
memo="Lightning Address fee",
internal=True,
extra={
"tag": "wallet_lightning_address_fee",
"wallet": wallet.source_wallet_id,
},
),
)
try:
await pay_invoice(
wallet_id=wallet.source_wallet_id,
payment_request=invoice.bolt11,
description="Lightning Address fee",
tag="wallet_lightning_address_fee",
extra={
"tag": "wallet_lightning_address_fee",
"fee_wallet": settings.lnbits_service_fee_wallet,
},
)
except PaymentError as exc:
raise ValueError(exc.message) from exc
def _blacklist_words() -> set[str]:
return {
word.strip().lower()
for word in settings.lnbits_wallet_lightning_address_blacklist
if word.strip()
}
def _uses_blacklisted_word(local_part: str) -> bool:
words = _blacklist_words()
if not words:
return False
segments = [segment for segment in re.split(r"[._-]+", local_part) if segment]
return local_part in words or any(segment in words for segment in segments)

View file

@ -23,7 +23,6 @@ from lnurl.models import LnurlResponseModel
from loguru import logger
from pydantic import ValidationError
from lnbits.core.crud.wallets import get_wallet_id_by_ln_address
from lnbits.core.models import Payment
from lnbits.core.models.lnurl import CreateLnurlPayment, LnurlScan
from lnbits.decorators import (
@ -63,10 +62,7 @@ async def api_wallet_lightning_address_response(
reason="Lightning addresses are not supported on this instance."
)
if await get_wallet_id_by_ln_address(username):
return await wallet_lightning_address_response(username, request)
return LnurlErrorResponse(reason="Lightning Address not found.")
return await wallet_lightning_address_response(username, request)
@lnurl_router.get(