Refactors type hints for clarity
Some checks failed
CI / lint (push) Has been cancelled
/ release (push) Has been cancelled
/ pullrequest (push) Has been cancelled

Updates type hints in `crud.py`, `helpers.py`, and `models.py` for improved readability and maintainability.

Replaces `Merchant | None` with `Optional[Merchant]` and `list[X]` with `List[X]` for consistency with standard Python typing practices.
This commit is contained in:
padreug 2025-11-04 00:59:55 +01:00
parent 429522adba
commit e8ddc4b697
5 changed files with 122 additions and 115 deletions

69
crud.py
View file

@ -1,4 +1,5 @@
import json import json
from typing import List, Optional, Tuple
from lnbits.helpers import urlsafe_short_hash from lnbits.helpers import urlsafe_short_hash
@ -43,7 +44,7 @@ async def create_merchant(user_id: str, m: PartialMerchant) -> Merchant:
async def update_merchant( async def update_merchant(
user_id: str, merchant_id: str, config: MerchantConfig user_id: str, merchant_id: str, config: MerchantConfig
) -> Merchant | None: ) -> Optional[Merchant]:
await db.execute( await db.execute(
f""" f"""
UPDATE nostrmarket.merchants SET meta = :meta, time = {db.timestamp_now} UPDATE nostrmarket.merchants SET meta = :meta, time = {db.timestamp_now}
@ -54,7 +55,7 @@ async def update_merchant(
return await get_merchant(user_id, merchant_id) return await get_merchant(user_id, merchant_id)
async def touch_merchant(user_id: str, merchant_id: str) -> Merchant | None: async def touch_merchant(user_id: str, merchant_id: str) -> Optional[Merchant]:
await db.execute( await db.execute(
f""" f"""
UPDATE nostrmarket.merchants SET time = {db.timestamp_now} UPDATE nostrmarket.merchants SET time = {db.timestamp_now}
@ -65,7 +66,7 @@ async def touch_merchant(user_id: str, merchant_id: str) -> Merchant | None:
return await get_merchant(user_id, merchant_id) return await get_merchant(user_id, merchant_id)
async def get_merchant(user_id: str, merchant_id: str) -> Merchant | None: async def get_merchant(user_id: str, merchant_id: str) -> Optional[Merchant]:
row: dict = await db.fetchone( row: dict = await db.fetchone(
"""SELECT * FROM nostrmarket.merchants WHERE user_id = :user_id AND id = :id""", """SELECT * FROM nostrmarket.merchants WHERE user_id = :user_id AND id = :id""",
{ {
@ -77,7 +78,7 @@ async def get_merchant(user_id: str, merchant_id: str) -> Merchant | None:
return Merchant.from_row(row) if row else None return Merchant.from_row(row) if row else None
async def get_merchant_by_pubkey(public_key: str) -> Merchant | None: async def get_merchant_by_pubkey(public_key: str) -> Optional[Merchant]:
row: dict = await db.fetchone( row: dict = await db.fetchone(
"""SELECT * FROM nostrmarket.merchants WHERE public_key = :public_key""", """SELECT * FROM nostrmarket.merchants WHERE public_key = :public_key""",
{"public_key": public_key}, {"public_key": public_key},
@ -86,7 +87,7 @@ async def get_merchant_by_pubkey(public_key: str) -> Merchant | None:
return Merchant.from_row(row) if row else None return Merchant.from_row(row) if row else None
async def get_merchants_ids_with_pubkeys() -> list[tuple[str, str]]: async def get_merchants_ids_with_pubkeys() -> List[Tuple[str, str]]:
rows: list[dict] = await db.fetchall( rows: list[dict] = await db.fetchall(
"""SELECT id, public_key FROM nostrmarket.merchants""", """SELECT id, public_key FROM nostrmarket.merchants""",
) )
@ -94,7 +95,7 @@ async def get_merchants_ids_with_pubkeys() -> list[tuple[str, str]]:
return [(row["id"], row["public_key"]) for row in rows] return [(row["id"], row["public_key"]) for row in rows]
async def get_merchant_for_user(user_id: str) -> Merchant | None: async def get_merchant_for_user(user_id: str) -> Optional[Merchant]:
row: dict = await db.fetchone( row: dict = await db.fetchone(
"""SELECT * FROM nostrmarket.merchants WHERE user_id = :user_id """, """SELECT * FROM nostrmarket.merchants WHERE user_id = :user_id """,
{"user_id": user_id}, {"user_id": user_id},
@ -137,7 +138,7 @@ async def create_zone(merchant_id: str, data: Zone) -> Zone:
return zone return zone
async def update_zone(merchant_id: str, z: Zone) -> Zone | None: async def update_zone(merchant_id: str, z: Zone) -> Optional[Zone]:
await db.execute( await db.execute(
""" """
UPDATE nostrmarket.zones UPDATE nostrmarket.zones
@ -156,7 +157,7 @@ async def update_zone(merchant_id: str, z: Zone) -> Zone | None:
return await get_zone(merchant_id, z.id) return await get_zone(merchant_id, z.id)
async def get_zone(merchant_id: str, zone_id: str) -> Zone | None: async def get_zone(merchant_id: str, zone_id: str) -> Optional[Zone]:
row: dict = await db.fetchone( row: dict = await db.fetchone(
"SELECT * FROM nostrmarket.zones WHERE merchant_id = :merchant_id AND id = :id", "SELECT * FROM nostrmarket.zones WHERE merchant_id = :merchant_id AND id = :id",
{ {
@ -167,7 +168,7 @@ async def get_zone(merchant_id: str, zone_id: str) -> Zone | None:
return Zone.from_row(row) if row else None return Zone.from_row(row) if row else None
async def get_zones(merchant_id: str) -> list[Zone]: async def get_zones(merchant_id: str) -> List[Zone]:
rows: list[dict] = await db.fetchall( rows: list[dict] = await db.fetchall(
"SELECT * FROM nostrmarket.zones WHERE merchant_id = :merchant_id", "SELECT * FROM nostrmarket.zones WHERE merchant_id = :merchant_id",
{"merchant_id": merchant_id}, {"merchant_id": merchant_id},
@ -234,7 +235,7 @@ async def create_stall(merchant_id: str, data: Stall) -> Stall:
return stall return stall
async def get_stall(merchant_id: str, stall_id: str) -> Stall | None: async def get_stall(merchant_id: str, stall_id: str) -> Optional[Stall]:
row: dict = await db.fetchone( row: dict = await db.fetchone(
""" """
SELECT * FROM nostrmarket.stalls SELECT * FROM nostrmarket.stalls
@ -248,7 +249,7 @@ async def get_stall(merchant_id: str, stall_id: str) -> Stall | None:
return Stall.from_row(row) if row else None return Stall.from_row(row) if row else None
async def get_stalls(merchant_id: str, pending: bool | None = False) -> list[Stall]: async def get_stalls(merchant_id: str, pending: Optional[bool] = False) -> List[Stall]:
rows: list[dict] = await db.fetchall( rows: list[dict] = await db.fetchall(
""" """
SELECT * FROM nostrmarket.stalls SELECT * FROM nostrmarket.stalls
@ -273,7 +274,7 @@ async def get_last_stall_update_time() -> int:
return row["event_created_at"] or 0 if row else 0 return row["event_created_at"] or 0 if row else 0
async def update_stall(merchant_id: str, stall: Stall) -> Stall | None: async def update_stall(merchant_id: str, stall: Stall) -> Optional[Stall]:
await db.execute( await db.execute(
""" """
UPDATE nostrmarket.stalls UPDATE nostrmarket.stalls
@ -397,7 +398,9 @@ async def update_product(merchant_id: str, product: Product) -> Product:
return updated_product return updated_product
async def update_product_quantity(product_id: str, new_quantity: int) -> Product | None: async def update_product_quantity(
product_id: str, new_quantity: int
) -> Optional[Product]:
await db.execute( await db.execute(
""" """
UPDATE nostrmarket.products SET quantity = :quantity UPDATE nostrmarket.products SET quantity = :quantity
@ -412,7 +415,7 @@ async def update_product_quantity(product_id: str, new_quantity: int) -> Product
return Product.from_row(row) if row else None return Product.from_row(row) if row else None
async def get_product(merchant_id: str, product_id: str) -> Product | None: async def get_product(merchant_id: str, product_id: str) -> Optional[Product]:
row: dict = await db.fetchone( row: dict = await db.fetchone(
""" """
SELECT * FROM nostrmarket.products SELECT * FROM nostrmarket.products
@ -428,8 +431,8 @@ async def get_product(merchant_id: str, product_id: str) -> Product | None:
async def get_products( async def get_products(
merchant_id: str, stall_id: str, pending: bool | None = False merchant_id: str, stall_id: str, pending: Optional[bool] = False
) -> list[Product]: ) -> List[Product]:
rows: list[dict] = await db.fetchall( rows: list[dict] = await db.fetchall(
""" """
SELECT * FROM nostrmarket.products SELECT * FROM nostrmarket.products
@ -442,8 +445,8 @@ async def get_products(
async def get_products_by_ids( async def get_products_by_ids(
merchant_id: str, product_ids: list[str] merchant_id: str, product_ids: List[str]
) -> list[Product]: ) -> List[Product]:
# todo: revisit # todo: revisit
keys = [] keys = []
@ -464,7 +467,7 @@ async def get_products_by_ids(
return [Product.from_row(row) for row in rows] return [Product.from_row(row) for row in rows]
async def get_wallet_for_product(product_id: str) -> str | None: async def get_wallet_for_product(product_id: str) -> Optional[str]:
row: dict = await db.fetchone( row: dict = await db.fetchone(
""" """
SELECT s.wallet as wallet FROM nostrmarket.products p SELECT s.wallet as wallet FROM nostrmarket.products p
@ -571,7 +574,7 @@ async def create_order(merchant_id: str, o: Order) -> Order:
return order return order
async def get_order(merchant_id: str, order_id: str) -> Order | None: async def get_order(merchant_id: str, order_id: str) -> Optional[Order]:
row: dict = await db.fetchone( row: dict = await db.fetchone(
""" """
SELECT * FROM nostrmarket.orders SELECT * FROM nostrmarket.orders
@ -585,7 +588,7 @@ async def get_order(merchant_id: str, order_id: str) -> Order | None:
return Order.from_row(row) if row else None return Order.from_row(row) if row else None
async def get_order_by_event_id(merchant_id: str, event_id: str) -> Order | None: async def get_order_by_event_id(merchant_id: str, event_id: str) -> Optional[Order]:
row: dict = await db.fetchone( row: dict = await db.fetchone(
""" """
SELECT * FROM nostrmarket.orders SELECT * FROM nostrmarket.orders
@ -599,7 +602,7 @@ async def get_order_by_event_id(merchant_id: str, event_id: str) -> Order | None
return Order.from_row(row) if row else None return Order.from_row(row) if row else None
async def get_orders(merchant_id: str, **kwargs) -> list[Order]: async def get_orders(merchant_id: str, **kwargs) -> List[Order]:
q = " AND ".join( q = " AND ".join(
[ [
f"{field[0]} = :{field[0]}" f"{field[0]} = :{field[0]}"
@ -626,7 +629,7 @@ async def get_orders(merchant_id: str, **kwargs) -> list[Order]:
async def get_orders_for_stall( async def get_orders_for_stall(
merchant_id: str, stall_id: str, **kwargs merchant_id: str, stall_id: str, **kwargs
) -> list[Order]: ) -> List[Order]:
q = " AND ".join( q = " AND ".join(
[ [
f"{field[0]} = :{field[0]}" f"{field[0]} = :{field[0]}"
@ -651,7 +654,7 @@ async def get_orders_for_stall(
return [Order.from_row(row) for row in rows] return [Order.from_row(row) for row in rows]
async def update_order(merchant_id: str, order_id: str, **kwargs) -> Order | None: async def update_order(merchant_id: str, order_id: str, **kwargs) -> Optional[Order]:
q = ", ".join( q = ", ".join(
[ [
f"{field[0]} = :{field[0]}" f"{field[0]} = :{field[0]}"
@ -675,7 +678,7 @@ async def update_order(merchant_id: str, order_id: str, **kwargs) -> Order | Non
return await get_order(merchant_id, order_id) return await get_order(merchant_id, order_id)
async def update_order_paid_status(order_id: str, paid: bool) -> Order | None: async def update_order_paid_status(order_id: str, paid: bool) -> Optional[Order]:
await db.execute( await db.execute(
"UPDATE nostrmarket.orders SET paid = :paid WHERE id = :id", "UPDATE nostrmarket.orders SET paid = :paid WHERE id = :id",
{"paid": paid, "id": order_id}, {"paid": paid, "id": order_id},
@ -689,7 +692,7 @@ async def update_order_paid_status(order_id: str, paid: bool) -> Order | None:
async def update_order_shipped_status( async def update_order_shipped_status(
merchant_id: str, order_id: str, shipped: bool merchant_id: str, order_id: str, shipped: bool
) -> Order | None: ) -> Optional[Order]:
await db.execute( await db.execute(
""" """
UPDATE nostrmarket.orders UPDATE nostrmarket.orders
@ -753,7 +756,7 @@ async def create_direct_message(
return msg return msg
async def get_direct_message(merchant_id: str, dm_id: str) -> DirectMessage | None: async def get_direct_message(merchant_id: str, dm_id: str) -> Optional[DirectMessage]:
row: dict = await db.fetchone( row: dict = await db.fetchone(
""" """
SELECT * FROM nostrmarket.direct_messages SELECT * FROM nostrmarket.direct_messages
@ -769,7 +772,7 @@ async def get_direct_message(merchant_id: str, dm_id: str) -> DirectMessage | No
async def get_direct_message_by_event_id( async def get_direct_message_by_event_id(
merchant_id: str, event_id: str merchant_id: str, event_id: str
) -> DirectMessage | None: ) -> Optional[DirectMessage]:
row: dict = await db.fetchone( row: dict = await db.fetchone(
""" """
SELECT * FROM nostrmarket.direct_messages SELECT * FROM nostrmarket.direct_messages
@ -783,7 +786,7 @@ async def get_direct_message_by_event_id(
return DirectMessage.from_row(row) if row else None return DirectMessage.from_row(row) if row else None
async def get_direct_messages(merchant_id: str, public_key: str) -> list[DirectMessage]: async def get_direct_messages(merchant_id: str, public_key: str) -> List[DirectMessage]:
rows: list[dict] = await db.fetchall( rows: list[dict] = await db.fetchall(
""" """
SELECT * FROM nostrmarket.direct_messages SELECT * FROM nostrmarket.direct_messages
@ -795,7 +798,7 @@ async def get_direct_messages(merchant_id: str, public_key: str) -> list[DirectM
return [DirectMessage.from_row(row) for row in rows] return [DirectMessage.from_row(row) for row in rows]
async def get_orders_from_direct_messages(merchant_id: str) -> list[DirectMessage]: async def get_orders_from_direct_messages(merchant_id: str) -> List[DirectMessage]:
rows: list[dict] = await db.fetchall( rows: list[dict] = await db.fetchall(
""" """
SELECT * FROM nostrmarket.direct_messages SELECT * FROM nostrmarket.direct_messages
@ -856,7 +859,7 @@ async def create_customer(merchant_id: str, data: Customer) -> Customer:
return customer return customer
async def get_customer(merchant_id: str, public_key: str) -> Customer | None: async def get_customer(merchant_id: str, public_key: str) -> Optional[Customer]:
row: dict = await db.fetchone( row: dict = await db.fetchone(
""" """
SELECT * FROM nostrmarket.customers SELECT * FROM nostrmarket.customers
@ -870,7 +873,7 @@ async def get_customer(merchant_id: str, public_key: str) -> Customer | None:
return Customer.from_row(row) if row else None return Customer.from_row(row) if row else None
async def get_customers(merchant_id: str) -> list[Customer]: async def get_customers(merchant_id: str) -> List[Customer]:
rows: list[dict] = await db.fetchall( rows: list[dict] = await db.fetchall(
"SELECT * FROM nostrmarket.customers WHERE merchant_id = :merchant_id", "SELECT * FROM nostrmarket.customers WHERE merchant_id = :merchant_id",
{"merchant_id": merchant_id}, {"merchant_id": merchant_id},
@ -878,7 +881,7 @@ async def get_customers(merchant_id: str) -> list[Customer]:
return [Customer.from_row(row) for row in rows] return [Customer.from_row(row) for row in rows]
async def get_all_unique_customers() -> list[Customer]: async def get_all_unique_customers() -> List[Customer]:
q = """ q = """
SELECT public_key, MAX(merchant_id) as merchant_id, MAX(event_created_at) SELECT public_key, MAX(merchant_id) as merchant_id, MAX(event_created_at)
FROM nostrmarket.customers FROM nostrmarket.customers

View file

@ -1,5 +1,6 @@
import base64 import base64
import secrets import secrets
from typing import Optional
import secp256k1 import secp256k1
from bech32 import bech32_decode, convertbits from bech32 import bech32_decode, convertbits
@ -32,7 +33,7 @@ def decrypt_message(encoded_message: str, encryption_key) -> str:
return unpadded_data.decode() return unpadded_data.decode()
def encrypt_message(message: str, encryption_key, iv: bytes | None = None) -> str: def encrypt_message(message: str, encryption_key, iv: Optional[bytes] = None) -> str:
padder = padding.PKCS7(128).padder() padder = padding.PKCS7(128).padder()
padded_data = padder.update(message.encode()) + padder.finalize() padded_data = padder.update(message.encode()) + padder.finalize()

116
models.py
View file

@ -2,7 +2,7 @@ import json
import time import time
from abc import abstractmethod from abc import abstractmethod
from enum import Enum from enum import Enum
from typing import Any from typing import Any, List, Optional, Tuple
from lnbits.utils.exchange_rates import btc_price, fiat_amount_as_satoshis from lnbits.utils.exchange_rates import btc_price, fiat_amount_as_satoshis
from pydantic import BaseModel from pydantic import BaseModel
@ -32,17 +32,17 @@ class Nostrable:
class MerchantProfile(BaseModel): class MerchantProfile(BaseModel):
name: str | None = None name: Optional[str] = None
about: str | None = None about: Optional[str] = None
picture: str | None = None picture: Optional[str] = None
class MerchantConfig(MerchantProfile): class MerchantConfig(MerchantProfile):
event_id: str | None = None event_id: Optional[str] = None
sync_from_nostr: bool = False sync_from_nostr = False
# TODO: switched to True for AIO demo; determine if we leave this as True # TODO: switched to True for AIO demo; determine if we leave this as True
active: bool = True active: bool = True
restore_in_progress: bool | None = False restore_in_progress: Optional[bool] = False
class CreateMerchantRequest(BaseModel): class CreateMerchantRequest(BaseModel):
@ -57,7 +57,7 @@ class PartialMerchant(BaseModel):
class Merchant(PartialMerchant, Nostrable): class Merchant(PartialMerchant, Nostrable):
id: str id: str
time: int | None = 0 time: Optional[int] = 0
def sign_hash(self, hash_: bytes) -> str: def sign_hash(self, hash_: bytes) -> str:
return sign_message_hash(self.private_key, hash_) return sign_message_hash(self.private_key, hash_)
@ -127,11 +127,11 @@ class Merchant(PartialMerchant, Nostrable):
######################################## ZONES ######################################## ######################################## ZONES ########################################
class Zone(BaseModel): class Zone(BaseModel):
id: str | None = None id: Optional[str] = None
name: str | None = None name: Optional[str] = None
currency: str currency: str
cost: float cost: float
countries: list[str] = [] countries: List[str] = []
@classmethod @classmethod
def from_row(cls, row: dict) -> "Zone": def from_row(cls, row: dict) -> "Zone":
@ -144,22 +144,22 @@ class Zone(BaseModel):
class StallConfig(BaseModel): class StallConfig(BaseModel):
image_url: str | None = None image_url: Optional[str] = None
description: str | None = None description: Optional[str] = None
class Stall(BaseModel, Nostrable): class Stall(BaseModel, Nostrable):
id: str | None = None id: Optional[str] = None
wallet: str wallet: str
name: str name: str
currency: str = "sat" currency: str = "sat"
shipping_zones: list[Zone] = [] shipping_zones: List[Zone] = []
config: StallConfig = StallConfig() config: StallConfig = StallConfig()
pending: bool = False pending: bool = False
"""Last published nostr event for this Stall""" """Last published nostr event for this Stall"""
event_id: str | None = None event_id: Optional[str] = None
event_created_at: int | None = None event_created_at: Optional[int] = None
def validate_stall(self): def validate_stall(self):
for z in self.shipping_zones: for z in self.shipping_zones:
@ -217,19 +217,19 @@ class ProductShippingCost(BaseModel):
class ProductConfig(BaseModel): class ProductConfig(BaseModel):
description: str | None = None description: Optional[str] = None
currency: str | None = None currency: Optional[str] = None
use_autoreply: bool | None = False use_autoreply: Optional[bool] = False
autoreply_message: str | None = None autoreply_message: Optional[str] = None
shipping: list[ProductShippingCost] = [] shipping: List[ProductShippingCost] = []
class Product(BaseModel, Nostrable): class Product(BaseModel, Nostrable):
id: str | None = None id: Optional[str] = None
stall_id: str stall_id: str
name: str name: str
categories: list[str] = [] categories: List[str] = []
images: list[str] = [] images: List[str] = []
price: float price: float
quantity: int quantity: int
active: bool = True active: bool = True
@ -237,8 +237,8 @@ class Product(BaseModel, Nostrable):
config: ProductConfig = ProductConfig() config: ProductConfig = ProductConfig()
"""Last published nostr event for this Product""" """Last published nostr event for this Product"""
event_id: str | None = None event_id: Optional[str] = None
event_created_at: int | None = None event_created_at: Optional[int] = None
def to_nostr_event(self, pubkey: str) -> NostrEvent: def to_nostr_event(self, pubkey: str) -> NostrEvent:
content = { content = {
@ -295,7 +295,7 @@ class ProductOverview(BaseModel):
id: str id: str
name: str name: str
price: float price: float
product_shipping_cost: float | None = None product_shipping_cost: Optional[float] = None
@classmethod @classmethod
def from_product(cls, p: Product) -> "ProductOverview": def from_product(cls, p: Product) -> "ProductOverview":
@ -312,21 +312,21 @@ class OrderItem(BaseModel):
class OrderContact(BaseModel): class OrderContact(BaseModel):
nostr: str | None = None nostr: Optional[str] = None
phone: str | None = None phone: Optional[str] = None
email: str | None = None email: Optional[str] = None
class OrderExtra(BaseModel): class OrderExtra(BaseModel):
products: list[ProductOverview] products: List[ProductOverview]
currency: str currency: str
btc_price: str btc_price: str
shipping_cost: float = 0 shipping_cost: float = 0
shipping_cost_sat: float = 0 shipping_cost_sat: float = 0
fail_message: str | None = None fail_message: Optional[str] = None
@classmethod @classmethod
async def from_products(cls, products: list[Product]): async def from_products(cls, products: List[Product]):
currency = products[0].config.currency if len(products) else "sat" currency = products[0].config.currency if len(products) else "sat"
exchange_rate = ( exchange_rate = (
await btc_price(currency) if currency and currency != "sat" else 1 await btc_price(currency) if currency and currency != "sat" else 1
@ -342,19 +342,19 @@ class OrderExtra(BaseModel):
class PartialOrder(BaseModel): class PartialOrder(BaseModel):
id: str id: str
event_id: str | None = None event_id: Optional[str] = None
event_created_at: int | None = None event_created_at: Optional[int] = None
public_key: str public_key: str
merchant_public_key: str merchant_public_key: str
shipping_id: str shipping_id: str
items: list[OrderItem] items: List[OrderItem]
contact: OrderContact | None = None contact: Optional[OrderContact] = None
address: str | None = None address: Optional[str] = None
def validate_order(self): def validate_order(self):
assert len(self.items) != 0, f"Order has no items. Order: '{self.id}'" assert len(self.items) != 0, f"Order has no items. Order: '{self.id}'"
def validate_order_items(self, product_list: list[Product]): def validate_order_items(self, product_list: List[Product]):
assert len(self.items) != 0, f"Order has no items. Order: '{self.id}'" assert len(self.items) != 0, f"Order has no items. Order: '{self.id}'"
assert ( assert (
len(product_list) != 0 len(product_list) != 0
@ -375,8 +375,8 @@ class PartialOrder(BaseModel):
) )
async def costs_in_sats( async def costs_in_sats(
self, products: list[Product], shipping_id: str, stall_shipping_cost: float self, products: List[Product], shipping_id: str, stall_shipping_cost: float
) -> tuple[float, float]: ) -> Tuple[float, float]:
product_prices = {} product_prices = {}
for p in products: for p in products:
product_shipping_cost = next( product_shipping_cost = next(
@ -405,7 +405,7 @@ class PartialOrder(BaseModel):
return product_cost, stall_shipping_cost return product_cost, stall_shipping_cost
def receipt( def receipt(
self, products: list[Product], shipping_id: str, stall_shipping_cost: float self, products: List[Product], shipping_id: str, stall_shipping_cost: float
) -> str: ) -> str:
if len(products) == 0: if len(products) == 0:
return "[No Products]" return "[No Products]"
@ -454,7 +454,7 @@ class Order(PartialOrder):
total: float total: float
paid: bool = False paid: bool = False
shipped: bool = False shipped: bool = False
time: int | None = None time: Optional[int] = None
extra: OrderExtra extra: OrderExtra
@classmethod @classmethod
@ -468,14 +468,14 @@ class Order(PartialOrder):
class OrderStatusUpdate(BaseModel): class OrderStatusUpdate(BaseModel):
id: str id: str
message: str | None = None message: Optional[str] = None
paid: bool | None = False paid: Optional[bool] = False
shipped: bool | None = None shipped: Optional[bool] = None
class OrderReissue(BaseModel): class OrderReissue(BaseModel):
id: str id: str
shipping_id: str | None = None shipping_id: Optional[str] = None
class PaymentOption(BaseModel): class PaymentOption(BaseModel):
@ -485,8 +485,8 @@ class PaymentOption(BaseModel):
class PaymentRequest(BaseModel): class PaymentRequest(BaseModel):
id: str id: str
message: str | None = None message: Optional[str] = None
payment_options: list[PaymentOption] payment_options: List[PaymentOption]
######################################## MESSAGE ####################################### ######################################## MESSAGE #######################################
@ -502,16 +502,16 @@ class DirectMessageType(Enum):
class PartialDirectMessage(BaseModel): class PartialDirectMessage(BaseModel):
event_id: str | None = None event_id: Optional[str] = None
event_created_at: int | None = None event_created_at: Optional[int] = None
message: str message: str
public_key: str public_key: str
type: int = DirectMessageType.PLAIN_TEXT.value type: int = DirectMessageType.PLAIN_TEXT.value
incoming: bool = False incoming: bool = False
time: int | None = None time: Optional[int] = None
@classmethod @classmethod
def parse_message(cls, msg) -> tuple[DirectMessageType, Any | None]: def parse_message(cls, msg) -> Tuple[DirectMessageType, Optional[Any]]:
try: try:
msg_json = json.loads(msg) msg_json = json.loads(msg)
if "type" in msg_json: if "type" in msg_json:
@ -534,15 +534,15 @@ class DirectMessage(PartialDirectMessage):
class CustomerProfile(BaseModel): class CustomerProfile(BaseModel):
name: str | None = None name: Optional[str] = None
about: str | None = None about: Optional[str] = None
class Customer(BaseModel): class Customer(BaseModel):
merchant_id: str merchant_id: str
public_key: str public_key: str
event_created_at: int | None = None event_created_at: Optional[int] = None
profile: CustomerProfile | None = None profile: Optional[CustomerProfile] = None
unread_messages: int = 0 unread_messages: int = 0
@classmethod @classmethod

View file

@ -1,7 +1,8 @@
import asyncio import asyncio
import json import json
from typing import List, Optional, Tuple
from bolt11 import decode from lnbits.bolt11 import decode
from lnbits.core.crud import get_wallet from lnbits.core.crud import get_wallet
from lnbits.core.services import create_invoice, websocket_updater from lnbits.core.services import create_invoice, websocket_updater
from loguru import logger from loguru import logger
@ -59,7 +60,7 @@ from .nostr.event import NostrEvent
async def create_new_order( async def create_new_order(
merchant_public_key: str, data: PartialOrder merchant_public_key: str, data: PartialOrder
) -> PaymentRequest | None: ) -> Optional[PaymentRequest]:
merchant = await get_merchant_by_pubkey(merchant_public_key) merchant = await get_merchant_by_pubkey(merchant_public_key)
assert merchant, "Cannot find merchant for order!" assert merchant, "Cannot find merchant for order!"
@ -144,7 +145,7 @@ async def update_merchant_to_nostr(
merchant: Merchant, delete_merchant=False merchant: Merchant, delete_merchant=False
) -> Merchant: ) -> Merchant:
stalls = await get_stalls(merchant.id) stalls = await get_stalls(merchant.id)
event: NostrEvent | None = None event: Optional[NostrEvent] = None
for stall in stalls: for stall in stalls:
assert stall.id assert stall.id
products = await get_products(merchant.id, stall.id) products = await get_products(merchant.id, stall.id)
@ -229,7 +230,7 @@ async def notify_client_of_order_status(
async def update_products_for_order( async def update_products_for_order(
merchant: Merchant, order: Order merchant: Merchant, order: Order
) -> tuple[bool, str]: ) -> Tuple[bool, str]:
product_ids = [i.product_id for i in order.items] product_ids = [i.product_id for i in order.items]
success, products, message = await compute_products_new_quantity( success, products, message = await compute_products_new_quantity(
merchant.id, product_ids, order.items merchant.id, product_ids, order.items
@ -297,9 +298,9 @@ async def send_dm(
async def compute_products_new_quantity( async def compute_products_new_quantity(
merchant_id: str, product_ids: list[str], items: list[OrderItem] merchant_id: str, product_ids: List[str], items: List[OrderItem]
) -> tuple[bool, list[Product], str]: ) -> Tuple[bool, List[Product], str]:
products: list[Product] = await get_products_by_ids(merchant_id, product_ids) products: List[Product] = await get_products_by_ids(merchant_id, product_ids)
for p in products: for p in products:
required_quantity = next( required_quantity = next(
@ -332,6 +333,7 @@ async def process_nostr_message(msg: str):
return return
_, event = rest _, event = rest
event = NostrEvent(**event) event = NostrEvent(**event)
if event.kind == 0: if event.kind == 0:
await _handle_customer_profile_update(event) await _handle_customer_profile_update(event)
elif event.kind == 4: elif event.kind == 4:
@ -502,7 +504,7 @@ async def _handle_outgoing_dms(
async def _handle_incoming_structured_dm( async def _handle_incoming_structured_dm(
merchant: Merchant, dm: DirectMessage, json_data: dict merchant: Merchant, dm: DirectMessage, json_data: dict
) -> tuple[DirectMessageType, str | None]: ) -> Tuple[DirectMessageType, Optional[str]]:
try: try:
if dm.type == DirectMessageType.CUSTOMER_ORDER.value and merchant.config.active: if dm.type == DirectMessageType.CUSTOMER_ORDER.value and merchant.config.active:
json_resp = await _handle_new_order( json_resp = await _handle_new_order(

View file

@ -1,12 +1,13 @@
import json import json
from http import HTTPStatus from http import HTTPStatus
from typing import List, Optional
from fastapi import Depends from fastapi import Depends
from fastapi.exceptions import HTTPException from fastapi.exceptions import HTTPException
from lnbits.core.models import WalletTypeInfo
from lnbits.core.crud import get_account, update_account from lnbits.core.crud import get_account, update_account
from lnbits.core.services import websocket_updater from lnbits.core.services import websocket_updater
from lnbits.decorators import ( from lnbits.decorators import (
WalletTypeInfo,
require_admin_key, require_admin_key,
require_invoice_key, require_invoice_key,
) )
@ -167,7 +168,7 @@ async def api_create_merchant(
@nostrmarket_ext.get("/api/v1/merchant") @nostrmarket_ext.get("/api/v1/merchant")
async def api_get_merchant( async def api_get_merchant(
wallet: WalletTypeInfo = Depends(require_invoice_key), wallet: WalletTypeInfo = Depends(require_invoice_key),
) -> Merchant | None: ) -> Optional[Merchant]:
try: try:
merchant = await get_merchant_for_user(wallet.wallet.user) merchant = await get_merchant_for_user(wallet.wallet.user)
@ -335,7 +336,7 @@ async def api_delete_merchant_on_nostr(
@nostrmarket_ext.get("/api/v1/zone") @nostrmarket_ext.get("/api/v1/zone")
async def api_get_zones( async def api_get_zones(
wallet: WalletTypeInfo = Depends(require_invoice_key), wallet: WalletTypeInfo = Depends(require_invoice_key),
) -> list[Zone]: ) -> List[Zone]:
try: try:
merchant = await get_merchant_for_user(wallet.wallet.user) merchant = await get_merchant_for_user(wallet.wallet.user)
assert merchant, "Merchant cannot be found" assert merchant, "Merchant cannot be found"
@ -535,7 +536,7 @@ async def api_get_stall(
@nostrmarket_ext.get("/api/v1/stall") @nostrmarket_ext.get("/api/v1/stall")
async def api_get_stalls( async def api_get_stalls(
pending: bool | None = False, pending: Optional[bool] = False,
wallet: WalletTypeInfo = Depends(require_invoice_key), wallet: WalletTypeInfo = Depends(require_invoice_key),
): ):
try: try:
@ -559,7 +560,7 @@ async def api_get_stalls(
@nostrmarket_ext.get("/api/v1/stall/product/{stall_id}") @nostrmarket_ext.get("/api/v1/stall/product/{stall_id}")
async def api_get_stall_products( async def api_get_stall_products(
stall_id: str, stall_id: str,
pending: bool | None = False, pending: Optional[bool] = False,
wallet: WalletTypeInfo = Depends(require_invoice_key), wallet: WalletTypeInfo = Depends(require_invoice_key),
): ):
try: try:
@ -583,9 +584,9 @@ async def api_get_stall_products(
@nostrmarket_ext.get("/api/v1/stall/order/{stall_id}") @nostrmarket_ext.get("/api/v1/stall/order/{stall_id}")
async def api_get_stall_orders( async def api_get_stall_orders(
stall_id: str, stall_id: str,
paid: bool | None = None, paid: Optional[bool] = None,
shipped: bool | None = None, shipped: Optional[bool] = None,
pubkey: str | None = None, pubkey: Optional[str] = None,
wallet: WalletTypeInfo = Depends(require_invoice_key), wallet: WalletTypeInfo = Depends(require_invoice_key),
): ):
try: try:
@ -719,7 +720,7 @@ async def api_update_product(
async def api_get_product( async def api_get_product(
product_id: str, product_id: str,
wallet: WalletTypeInfo = Depends(require_invoice_key), wallet: WalletTypeInfo = Depends(require_invoice_key),
) -> Product | None: ) -> Optional[Product]:
try: try:
merchant = await get_merchant_for_user(wallet.wallet.user) merchant = await get_merchant_for_user(wallet.wallet.user)
assert merchant, "Merchant cannot be found" assert merchant, "Merchant cannot be found"
@ -804,9 +805,9 @@ async def api_get_order(
@nostrmarket_ext.get("/api/v1/order") @nostrmarket_ext.get("/api/v1/order")
async def api_get_orders( async def api_get_orders(
paid: bool | None = None, paid: Optional[bool] = None,
shipped: bool | None = None, shipped: Optional[bool] = None,
pubkey: str | None = None, pubkey: Optional[str] = None,
wallet: WalletTypeInfo = Depends(require_invoice_key), wallet: WalletTypeInfo = Depends(require_invoice_key),
): ):
try: try:
@ -892,7 +893,7 @@ async def api_update_order_status(
async def api_restore_order( async def api_restore_order(
event_id: str, event_id: str,
wallet: WalletTypeInfo = Depends(require_admin_key), wallet: WalletTypeInfo = Depends(require_admin_key),
) -> Order | None: ) -> Optional[Order]:
try: try:
merchant = await get_merchant_for_user(wallet.wallet.user) merchant = await get_merchant_for_user(wallet.wallet.user)
assert merchant, "Merchant cannot be found" assert merchant, "Merchant cannot be found"
@ -1019,7 +1020,7 @@ async def api_reissue_order_invoice(
@nostrmarket_ext.get("/api/v1/message/{public_key}") @nostrmarket_ext.get("/api/v1/message/{public_key}")
async def api_get_messages( async def api_get_messages(
public_key: str, wallet: WalletTypeInfo = Depends(require_invoice_key) public_key: str, wallet: WalletTypeInfo = Depends(require_invoice_key)
) -> list[DirectMessage]: ) -> List[DirectMessage]:
try: try:
merchant = await get_merchant_for_user(wallet.wallet.user) merchant = await get_merchant_for_user(wallet.wallet.user)
assert merchant, "Merchant cannot be found" assert merchant, "Merchant cannot be found"
@ -1075,7 +1076,7 @@ async def api_create_message(
@nostrmarket_ext.get("/api/v1/customer") @nostrmarket_ext.get("/api/v1/customer")
async def api_get_customers( async def api_get_customers(
wallet: WalletTypeInfo = Depends(require_invoice_key), wallet: WalletTypeInfo = Depends(require_invoice_key),
) -> list[Customer]: ) -> List[Customer]:
try: try:
merchant = await get_merchant_for_user(wallet.wallet.user) merchant = await get_merchant_for_user(wallet.wallet.user)
assert merchant, "Merchant cannot be found" assert merchant, "Merchant cannot be found"