add Nostr publishing and bidirectional sync
- nostr/ vendors NostrEvent + the nostrclient WebSocket bridge from the events extension, retagged [TASKS] / subscription-id "tasks-*". - nostr_publisher builds kind 31922 with the `event-type: task` tag (per aiolabs/webapp#25 — disambiguates from kind-31922 activities on shared relays), kind 31925 with task-status / occurrence / completed_at, and kind 5 deletions for both. - nostr_hooks bridges task/completion mutations to the publisher and persists the resulting nostr_event_id back onto the local row. - nostr_sync subscribes to {31922, 31925, 5/#k} and filters 31922 client-side on `event-type: task` because most relays don't index custom single-letter tags. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
6fbb6d4a42
commit
24acbe6674
6 changed files with 755 additions and 0 deletions
97
nostr_hooks.py
Normal file
97
nostr_hooks.py
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
"""Helpers that bridge task-mutation handlers to the Nostr publisher.
|
||||
|
||||
Sits between views_api and nostr_publisher so we don't pull the publisher
|
||||
through the views module (which would create an import cycle via models)."""
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from .crud import update_task
|
||||
from .models import Task, TaskCompletion
|
||||
from .nostr_publisher import (
|
||||
publish_completion_delete_to_nostr,
|
||||
publish_completion_to_nostr,
|
||||
publish_task_to_nostr,
|
||||
)
|
||||
|
||||
|
||||
async def _account_keys(wallet_id: str) -> tuple[str, str] | None:
|
||||
"""Fetch (pubkey, prvkey) for the wallet's owning account. Returns None
|
||||
when the account is missing keys, so callers can skip cleanly."""
|
||||
from lnbits.core.crud.users import get_account
|
||||
from lnbits.core.crud.wallets import get_wallet
|
||||
|
||||
wallet_obj = await get_wallet(wallet_id)
|
||||
if not wallet_obj:
|
||||
return None
|
||||
account = await get_account(wallet_obj.user)
|
||||
if not account or not account.pubkey or not account.prvkey: # type: ignore[attr-defined]
|
||||
return None
|
||||
return account.pubkey, account.prvkey # type: ignore[attr-defined]
|
||||
|
||||
|
||||
async def publish_or_delete_task_event(
|
||||
task: Task, *, delete: bool = False
|
||||
) -> None:
|
||||
"""Publish (or delete-publish) the NIP-52 kind 31922 for `task`.
|
||||
|
||||
Errors are logged and swallowed so a Nostr outage doesn't break the
|
||||
HTTP flow that triggered the publish."""
|
||||
try:
|
||||
from . import nostr_client
|
||||
|
||||
keys = await _account_keys(task.wallet)
|
||||
if not keys:
|
||||
return
|
||||
pubkey, prvkey = keys
|
||||
|
||||
nostr_event = await publish_task_to_nostr(
|
||||
nostr_client, task, pubkey, prvkey, delete=delete
|
||||
)
|
||||
if nostr_event and not delete:
|
||||
task.nostr_event_id = nostr_event.id
|
||||
task.nostr_event_created_at = nostr_event.created_at
|
||||
await update_task(task)
|
||||
except Exception as exc:
|
||||
logger.warning(f"[TASKS] Nostr task publish failed: {exc}")
|
||||
|
||||
|
||||
async def publish_task_completion(
|
||||
task: Task, completion: TaskCompletion
|
||||
) -> str | None:
|
||||
"""Publish a kind 31925 completion. Returns the Nostr event id so the
|
||||
caller can persist it as the completion's primary key, replacing the
|
||||
locally-generated hash from the optimistic insert."""
|
||||
try:
|
||||
from . import nostr_client
|
||||
|
||||
keys = await _account_keys(task.wallet)
|
||||
if not keys:
|
||||
return None
|
||||
pubkey, prvkey = keys
|
||||
|
||||
nostr_event = await publish_completion_to_nostr(
|
||||
nostr_client, task.address, completion, pubkey, prvkey
|
||||
)
|
||||
return nostr_event.id if nostr_event else None
|
||||
except Exception as exc:
|
||||
logger.warning(f"[TASKS] Nostr completion publish failed: {exc}")
|
||||
return None
|
||||
|
||||
|
||||
async def publish_completion_delete(
|
||||
wallet_id: str, completion_id: str
|
||||
) -> None:
|
||||
"""Publish a NIP-09 delete for a previously-published completion."""
|
||||
try:
|
||||
from . import nostr_client
|
||||
|
||||
keys = await _account_keys(wallet_id)
|
||||
if not keys:
|
||||
return
|
||||
pubkey, prvkey = keys
|
||||
|
||||
await publish_completion_delete_to_nostr(
|
||||
nostr_client, completion_id, pubkey, prvkey
|
||||
)
|
||||
except Exception as exc:
|
||||
logger.warning(f"[TASKS] Nostr completion delete failed: {exc}")
|
||||
Loading…
Add table
Add a link
Reference in a new issue