feat: on meta update, replace old meta
This commit is contained in:
parent
57197b981d
commit
10ef9ee2ac
5 changed files with 56 additions and 15 deletions
|
|
@ -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
10
crud.py
|
|
@ -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}"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue