feat: on meta update, replace old meta

This commit is contained in:
Vlad Stan 2023-02-06 11:10:14 +02:00
parent 57197b981d
commit 10ef9ee2ac
5 changed files with 56 additions and 15 deletions

View file

@ -4,7 +4,13 @@ from typing import Any, Callable, List
from fastapi import WebSocket from fastapi import WebSocket
from loguru import logger from loguru import logger
from .crud import create_event, get_event, get_events, mark_events_deleted from .crud import (
create_event,
delete_events,
get_event,
get_events,
mark_events_deleted,
)
from .models import NostrEvent, NostrEventType, NostrFilter from .models import NostrEvent, NostrEventType, NostrFilter
@ -36,7 +42,7 @@ class NostrClientConnection:
await self.websocket.accept() await self.websocket.accept()
while True: while True:
json_data = await self.websocket.receive_text() json_data = await self.websocket.receive_text()
print('### received: ', json_data) print("### received: ", json_data)
try: try:
data = json.loads(json_data) data = json.loads(json_data)
@ -53,7 +59,6 @@ class NostrClientConnection:
await self.websocket.send_text(json.dumps(resp)) await self.websocket.send_text(json.dumps(resp))
return True return True
return False return False
async def __handle_message(self, data: List) -> List: async def __handle_message(self, data: List) -> List:
if len(data) < 2: if len(data) < 2:
@ -76,10 +81,12 @@ class NostrClientConnection:
resp_nip20: List[Any] = ["OK", e.id] resp_nip20: List[Any] = ["OK", e.id]
try: try:
e.check_signature() e.check_signature()
if e.is_meta_event():
await delete_events("111", NostrFilter(kinds=[0], authors=[e.pubkey]))
await create_event("111", e) await create_event("111", e)
await self.broadcast_event(self, e) await self.broadcast_event(self, e)
if e.is_delete_event(): if e.is_delete_event():
await self.__delete_event(e) await self.__handle_delete_event(e)
resp_nip20 += [True, ""] resp_nip20 += [True, ""]
except ValueError: except ValueError:
resp_nip20 += [False, "invalid: wrong event `id` or `sig`"] resp_nip20 += [False, "invalid: wrong event `id` or `sig`"]
@ -90,7 +97,7 @@ class NostrClientConnection:
await self.websocket.send_text(json.dumps(resp_nip20)) await self.websocket.send_text(json.dumps(resp_nip20))
async def __delete_event(self, event: NostrEvent): async def __handle_delete_event(self, event: NostrEvent):
# NIP 09 # NIP 09
filter = NostrFilter(authors=[event.pubkey]) filter = NostrFilter(authors=[event.pubkey])
filter.ids = [t[1] for t in event.tags if t[0] == "e"] filter.ids = [t[1] for t in event.tags if t[0] == "e"]

10
crud.py
View file

@ -60,6 +60,14 @@ async def mark_events_deleted(relay_id: str, filter: NostrFilter):
await db.execute(f"""UPDATE nostrrelay.events SET deleted=true WHERE {" AND ".join(where)}""", tuple(values)) await db.execute(f"""UPDATE nostrrelay.events SET deleted=true WHERE {" AND ".join(where)}""", tuple(values))
async def delete_events(relay_id: str, filter: NostrFilter):
if filter.is_empty():
return None
_, where, values = build_where_clause(relay_id, filter)
query = f"""DELETE from nostrrelay.events WHERE {" AND ".join(where)}"""
await db.execute(query, tuple(values))
async def create_event_tags( async def create_event_tags(
relay_id: str, event_id: str, tag_name: str, tag_value: str, extra_values: Optional[str] relay_id: str, event_id: str, tag_name: str, tag_value: str, extra_values: Optional[str]
@ -109,7 +117,7 @@ def build_select_events_query(relay_id:str, filter:NostrFilter):
ORDER BY created_at DESC ORDER BY created_at DESC
""" """
# todo: check range # todo: check & enforce range
if filter.limit and filter.limit > 0: if filter.limit and filter.limit > 0:
query += f" LIMIT {filter.limit}" query += f" LIMIT {filter.limit}"

View file

@ -59,6 +59,9 @@ class NostrEvent(BaseModel):
id = hashlib.sha256(data.encode()).hexdigest() id = hashlib.sha256(data.encode()).hexdigest()
return id return id
def is_meta_event(self) -> bool:
return self.kind == 0
def is_delete_event(self) -> bool: def is_delete_event(self) -> bool:
return self.kind == 5 return self.kind == 5

View file

@ -18,6 +18,24 @@
true, true,
"" ""
], ],
"meta_update": [
"EVENT",
{
"id": "2928f73760ac3a60affdf51d04169680472a8594b4584f087f497dcf6a28d12a",
"pubkey": "0b29ecc73ba400e5b4bd1e4cb0d8f524e9958345749197ca21c8da38d0622816",
"created_at": 1675673494,
"kind": 0,
"tags": [],
"content": "{\"name\":\"Alice\",\"about\":\"Uses Hamstr\"}",
"sig": "938313418d6d8b16b43213b3347c64925cbc1846e4447b4d878be9b865fe4b78f276ac399ea6b0aa81ed88fb18c992f2fae9e4f70c35c49202e576c54a0dc89c"
}
],
"meta_update_response": [
"OK",
"2928f73760ac3a60affdf51d04169680472a8594b4584f087f497dcf6a28d12a",
true,
""
],
"post01": [ "post01": [
"EVENT", "EVENT",
{ {

View file

@ -1,7 +1,8 @@
import asyncio import asyncio
from json import dumps, loads
import pytest import pytest
from json import dumps, loads
from copy import deepcopy
from fastapi import WebSocket from fastapi import WebSocket
from lnbits.extensions.nostrrelay.client_manager import ( from lnbits.extensions.nostrrelay.client_manager import (
@ -80,10 +81,11 @@ async def alice_wires_meta_and_post01(ws_alice: MockWebSocket):
await ws_alice.wire_mock_data(alice["meta"]) await ws_alice.wire_mock_data(alice["meta"])
await ws_alice.wire_mock_data(alice["post01"]) await ws_alice.wire_mock_data(alice["post01"])
await ws_alice.wire_mock_data(alice["post01"]) await ws_alice.wire_mock_data(alice["post01"])
await ws_alice.wire_mock_data(alice["meta_update"])
await asyncio.sleep(0.5) await asyncio.sleep(0.5)
assert ( assert (
len(ws_alice.sent_messages) == 3 len(ws_alice.sent_messages) == 4
), "Alice: Expected 3 confirmations to be sent" ), "Alice: Expected 3 confirmations to be sent"
assert ws_alice.sent_messages[0] == dumps( assert ws_alice.sent_messages[0] == dumps(
alice["meta_response"] alice["meta_response"]
@ -94,6 +96,9 @@ async def alice_wires_meta_and_post01(ws_alice: MockWebSocket):
assert ws_alice.sent_messages[2] == dumps( assert ws_alice.sent_messages[2] == dumps(
alice["post01_response_duplicate"] alice["post01_response_duplicate"]
), "Alice: Expected failure for double posting" ), "Alice: Expected failure for double posting"
assert ws_alice.sent_messages[3] == dumps(
alice["meta_update_response"]
), "Alice: Expected confirmation for meta update"
await asyncio.sleep(0.1) await asyncio.sleep(0.1)
@ -112,8 +117,8 @@ async def bob_wires_meta_and_folows_alice(ws_bob: MockWebSocket):
bob["meta_response"] bob["meta_response"]
), "Bob: Wrong confirmation for meta" ), "Bob: Wrong confirmation for meta"
assert ws_bob.sent_messages[1] == dumps( assert ws_bob.sent_messages[1] == dumps(
["EVENT", "profile", alice["meta"][1]] ["EVENT", "profile", alice["meta_update"][1]]
), "Bob: Wrong response for Alice's meta" ), "Bob: Wrong response for Alice's meta (updated version)"
assert ws_bob.sent_messages[2] == dumps( assert ws_bob.sent_messages[2] == dumps(
["EOSE", "profile"] ["EOSE", "profile"]
), "Bob: Wrong End Of Streaming Event for profile" ), "Bob: Wrong End Of Streaming Event for profile"
@ -266,7 +271,10 @@ async def alice_writes_to_bob(ws_alice: MockWebSocket, ws_bob: MockWebSocket):
["EOSE", "notifications:d685447c43c7c18dbbea61923cf0b63e1ab46bed"] ["EOSE", "notifications:d685447c43c7c18dbbea61923cf0b63e1ab46bed"]
), "Bob: Received all stored events" ), "Bob: Received all stored events"
async def alice_deletes_post01__bob_is_notified(ws_alice: MockWebSocket, ws_bob:MockWebSocket):
async def alice_deletes_post01__bob_is_notified(
ws_alice: MockWebSocket, ws_bob: MockWebSocket
):
ws_bob.sent_messages.clear() ws_bob.sent_messages.clear()
await ws_bob.wire_mock_data(bob["request_posts_alice"]) await ws_bob.wire_mock_data(bob["request_posts_alice"])
await asyncio.sleep(0.1) await asyncio.sleep(0.1)
@ -305,6 +313,3 @@ async def alice_deletes_post01__bob_is_notified(ws_alice: MockWebSocket, ws_bob:
assert ( assert (
len(ws_bob.sent_messages) == 2 len(ws_bob.sent_messages) == 2
), "Bob: Expected one posts from Alice plus and EOSE" ), "Bob: Expected one posts from Alice plus and EOSE"