mirror of
https://github.com/aljazceru/python-nostr.git
synced 2026-02-23 15:14:19 +01:00
update client
This commit is contained in:
12
main.py
12
main.py
@@ -7,6 +7,14 @@ import time
|
|||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
|
||||||
|
def print_status(client):
|
||||||
|
print("")
|
||||||
|
for relay in client.relay_manager.relays.values():
|
||||||
|
connected_text = "🟢" if relay.connected else "🔴"
|
||||||
|
status_text = f"{connected_text} ⬆️ {relay.num_sent_events} ⬇️ {relay.num_received_events} ⚠️ {relay.error_counter} ⏱️ {relay.ping} ms - {relay.url.split('//')[1]}"
|
||||||
|
print(status_text)
|
||||||
|
|
||||||
|
|
||||||
async def dm():
|
async def dm():
|
||||||
print("This is an example NIP-04 DM flow")
|
print("This is an example NIP-04 DM flow")
|
||||||
pk = input("Enter your privatekey to post from (enter nothing for a random one): ")
|
pk = input("Enter your privatekey to post from (enter nothing for a random one): ")
|
||||||
@@ -36,6 +44,7 @@ async def dm():
|
|||||||
)
|
)
|
||||||
print(f"Subscribing to DMs to {to_pubk_hex}")
|
print(f"Subscribing to DMs to {to_pubk_hex}")
|
||||||
while True:
|
while True:
|
||||||
|
print_status(client)
|
||||||
msg = input("\nEnter message: ")
|
msg = input("\nEnter message: ")
|
||||||
client.dm(msg, PublicKey(bytes.fromhex(to_pubk_hex)))
|
client.dm(msg, PublicKey(bytes.fromhex(to_pubk_hex)))
|
||||||
|
|
||||||
@@ -84,11 +93,12 @@ async def post():
|
|||||||
t.start()
|
t.start()
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
print_status(sender_client)
|
||||||
msg = input("\nEnter post: ")
|
msg = input("\nEnter post: ")
|
||||||
sender_client.post(msg)
|
sender_client.post(msg)
|
||||||
|
|
||||||
|
|
||||||
if input("Enter 1 for DM, 2 for Posts (Default: 1)") or 1 == 1:
|
if input("Enter '1' for DM, '2' for Posts (Default: 1):") or 1 == 1:
|
||||||
# write a DM and receive DMs
|
# write a DM and receive DMs
|
||||||
asyncio.run(dm())
|
asyncio.run(dm())
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ from typing import List
|
|||||||
from secp256k1 import PrivateKey, PublicKey
|
from secp256k1 import PrivateKey, PublicKey
|
||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
|
|
||||||
from nostr.message_type import ClientMessageType
|
from .message_type import ClientMessageType
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class EventKind(IntEnum):
|
class EventKind(IntEnum):
|
||||||
@@ -19,17 +18,17 @@ class EventKind(IntEnum):
|
|||||||
DELETE = 5
|
DELETE = 5
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Event:
|
class Event:
|
||||||
content: str = None
|
content: str = None
|
||||||
public_key: str = None
|
public_key: str = None
|
||||||
created_at: int = None
|
created_at: int = None
|
||||||
kind: int = EventKind.TEXT_NOTE
|
kind: int = EventKind.TEXT_NOTE
|
||||||
tags: List[List[str]] = field(default_factory=list) # Dataclasses require special handling when the default value is a mutable type
|
tags: List[List[str]] = field(
|
||||||
|
default_factory=list
|
||||||
|
) # Dataclasses require special handling when the default value is a mutable type
|
||||||
signature: str = None
|
signature: str = None
|
||||||
|
|
||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
if self.content is not None and not isinstance(self.content, str):
|
if self.content is not None and not isinstance(self.content, str):
|
||||||
# DMs initialize content to None but all other kinds should pass in a str
|
# DMs initialize content to None but all other kinds should pass in a str
|
||||||
@@ -38,39 +37,44 @@ class Event:
|
|||||||
if self.created_at is None:
|
if self.created_at is None:
|
||||||
self.created_at = int(time.time())
|
self.created_at = int(time.time())
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def serialize(public_key: str, created_at: int, kind: int, tags: List[List[str]], content: str) -> bytes:
|
def serialize(
|
||||||
|
public_key: str, created_at: int, kind: int, tags: List[List[str]], content: str
|
||||||
|
) -> bytes:
|
||||||
data = [0, public_key, created_at, kind, tags, content]
|
data = [0, public_key, created_at, kind, tags, content]
|
||||||
data_str = json.dumps(data, separators=(',', ':'), ensure_ascii=False)
|
data_str = json.dumps(data, separators=(",", ":"), ensure_ascii=False)
|
||||||
return data_str.encode()
|
return data_str.encode()
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def compute_id(public_key: str, created_at: int, kind: int, tags: List[List[str]], content: str):
|
def compute_id(
|
||||||
return sha256(Event.serialize(public_key, created_at, kind, tags, content)).hexdigest()
|
public_key: str, created_at: int, kind: int, tags: List[List[str]], content: str
|
||||||
|
):
|
||||||
|
return sha256(
|
||||||
|
Event.serialize(public_key, created_at, kind, tags, content)
|
||||||
|
).hexdigest()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def id(self) -> str:
|
def id(self) -> str:
|
||||||
# Always recompute the id to reflect the up-to-date state of the Event
|
# Always recompute the id to reflect the up-to-date state of the Event
|
||||||
return Event.compute_id(self.public_key, self.created_at, self.kind, self.tags, self.content)
|
return Event.compute_id(
|
||||||
|
self.public_key, self.created_at, self.kind, self.tags, self.content
|
||||||
|
)
|
||||||
|
|
||||||
|
def add_pubkey_ref(self, pubkey: str):
|
||||||
|
"""Adds a reference to a pubkey as a 'p' tag"""
|
||||||
|
self.tags.append(["p", pubkey])
|
||||||
|
|
||||||
def add_pubkey_ref(self, pubkey:str):
|
def add_event_ref(self, event_id: str):
|
||||||
""" Adds a reference to a pubkey as a 'p' tag """
|
"""Adds a reference to an event_id as an 'e' tag"""
|
||||||
self.tags.append(['p', pubkey])
|
self.tags.append(["e", event_id])
|
||||||
|
|
||||||
|
|
||||||
def add_event_ref(self, event_id:str):
|
|
||||||
""" Adds a reference to an event_id as an 'e' tag """
|
|
||||||
self.tags.append(['e', event_id])
|
|
||||||
|
|
||||||
|
|
||||||
def verify(self) -> bool:
|
def verify(self) -> bool:
|
||||||
pub_key = PublicKey(bytes.fromhex("02" + self.public_key), True) # add 02 for schnorr (bip340)
|
pub_key = PublicKey(
|
||||||
return pub_key.schnorr_verify(bytes.fromhex(self.id), bytes.fromhex(self.signature), None, raw=True)
|
bytes.fromhex("02" + self.public_key), True
|
||||||
|
) # add 02 for schnorr (bip340)
|
||||||
|
return pub_key.schnorr_verify(
|
||||||
|
bytes.fromhex(self.id), bytes.fromhex(self.signature), None, raw=True
|
||||||
|
)
|
||||||
|
|
||||||
def to_message(self) -> str:
|
def to_message(self) -> str:
|
||||||
return json.dumps(
|
return json.dumps(
|
||||||
@@ -83,20 +87,18 @@ class Event:
|
|||||||
"kind": self.kind,
|
"kind": self.kind,
|
||||||
"tags": self.tags,
|
"tags": self.tags,
|
||||||
"content": self.content,
|
"content": self.content,
|
||||||
"sig": self.signature
|
"sig": self.signature,
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class EncryptedDirectMessage(Event):
|
class EncryptedDirectMessage(Event):
|
||||||
recipient_pubkey: str = None
|
recipient_pubkey: str = None
|
||||||
cleartext_content: str = None
|
cleartext_content: str = None
|
||||||
reference_event_id: str = None
|
reference_event_id: str = None
|
||||||
|
|
||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
if self.content is not None:
|
if self.content is not None:
|
||||||
self.cleartext_content = self.content
|
self.cleartext_content = self.content
|
||||||
@@ -115,9 +117,10 @@ class EncryptedDirectMessage(Event):
|
|||||||
if self.reference_event_id is not None:
|
if self.reference_event_id is not None:
|
||||||
self.add_event_ref(self.reference_event_id)
|
self.add_event_ref(self.reference_event_id)
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def id(self) -> str:
|
def id(self) -> str:
|
||||||
if self.content is None:
|
if self.content is None:
|
||||||
raise Exception("EncryptedDirectMessage `id` is undefined until its message is encrypted and stored in the `content` field")
|
raise Exception(
|
||||||
|
"EncryptedDirectMessage `id` is undefined until its message is encrypted and stored in the `content` field"
|
||||||
|
)
|
||||||
return super().id
|
return super().id
|
||||||
|
|||||||
@@ -78,10 +78,8 @@ class Relay:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def ping(self):
|
def ping(self):
|
||||||
if self.connected:
|
ping_ms = int((self.ws.last_pong_tm - self.ws.last_ping_tm) * 1000)
|
||||||
return int((self.ws.last_pong_tm - self.ws.last_ping_tm) * 1000)
|
return ping_ms if self.connected and ping_ms > 0 else 0
|
||||||
else:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
def publish(self, message: str):
|
def publish(self, message: str):
|
||||||
self.queue.put(message)
|
self.queue.put(message)
|
||||||
|
|||||||
Reference in New Issue
Block a user