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.
36 lines
936 B
Python
36 lines
936 B
Python
"""
|
|
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()
|