mirror of
https://github.com/lnbits/lnbits.git
synced 2026-08-13 12:42:47 +02:00
refactor: clean public functions
This commit is contained in:
parent
e2329b1453
commit
dbcf2cce59
3 changed files with 119 additions and 138 deletions
|
|
@ -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(
|
||||
*,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue