feat(nostr): NIP-99 menu listings, NIP-01 profile, NIP-17 stub
nostr/event.py
Bare NIP-01 NostrEvent with canonical id computation.
nostr/nostr_client.py
Bidirectional WebSocket client (lifted from events ext, kept
local). Connects to nostrclient ext's internal relay endpoint,
dedups by event id (LRU 1000).
nostr_publisher.py
Builders for:
* kind 0 — restaurant profile (NIP-01 metadata)
* kind 30402 — menu item (NIP-99 classified listing,
parameterized replaceable by item.id)
* kind 5 — deletion request (NIP-09)
Schnorr signing via coincurve (BIP-340).
Menu listings carry structured price tags (["price", n, currency]),
status (active|sold) so customers see sold-out items, and 't' tags
for category, dietary, allergens (allergen:<x>) and ingredients
(ingr:<x>) so webapps can filter without parsing markdown.
Restaurants can sign with their own keypair (per-restaurant Nostr
identity) or fall back to the LNbits Account keypair.
nostr_sync.py
Subscribes to:
* kind 30402 #t=menu — backfill 200 + live (echo confirmation
for now; foreign-menu indexing deferred until we settle on a
federated cache table).
* kind 1059 — NIP-17 gift-wrapped DMs, only when
settings.nostr_orders_enabled. Decryption stubbed (needs
NIP-44 v2 unwrap); REST stays the supported transport
until that's wired up. _place_order_from_dm is complete and
ready for the decryption hook.
This commit is contained in:
parent
201c387722
commit
b155548036
5 changed files with 601 additions and 0 deletions
36
nostr/event.py
Normal file
36
nostr/event.py
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
"""
|
||||
Bare NIP-01 event model.
|
||||
|
||||
Same shape as the events extension; kept independent so the two
|
||||
extensions can evolve their Nostr payloads without coupling.
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
from typing import List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class NostrEvent(BaseModel):
|
||||
id: str = ""
|
||||
pubkey: str
|
||||
created_at: int
|
||||
kind: int
|
||||
tags: List[List[str]] = []
|
||||
content: str = ""
|
||||
sig: Optional[str] = None
|
||||
|
||||
def serialize(self) -> List:
|
||||
# Per NIP-01, the canonical event id is sha256 of:
|
||||
# [0, pubkey, created_at, kind, tags, content]
|
||||
return [0, self.pubkey, self.created_at, self.kind, self.tags, self.content]
|
||||
|
||||
def serialize_json(self) -> str:
|
||||
return json.dumps(
|
||||
self.serialize(), separators=(",", ":"), ensure_ascii=False
|
||||
)
|
||||
|
||||
@property
|
||||
def event_id(self) -> str:
|
||||
return hashlib.sha256(self.serialize_json().encode()).hexdigest()
|
||||
Loading…
Add table
Add a link
Reference in a new issue