Skip to content

Commit 7296a41

Browse files
Convert npub
1 parent a740cfe commit 7296a41

File tree

2 files changed

+152
-1
lines changed

2 files changed

+152
-1
lines changed

internal_filesystem/apps/com.micropythonos.nostr/assets/nostr_client.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,18 @@ async def async_event_manager_task(self):
8686
self.subscription_id = "micropython_nostr_" + str(round(time.time()))
8787
print(f"DEBUG: Setting up subscription with ID: {self.subscription_id}")
8888

89+
# Convert npub to hex if needed
90+
follow_npub_hex = self.follow_npub
91+
if self.follow_npub.startswith("npub1"):
92+
from nostr.key import PublicKey
93+
follow_npub_hex = PublicKey.from_npub(self.follow_npub).hex()
94+
print(f"DEBUG: Converted npub to hex: {follow_npub_hex}")
95+
8996
# Create filter for events from follow_npub
9097
# Note: Some relays don't support filtering by both kinds and authors
9198
# So we just filter by authors
9299
self.filters = Filters([Filter(
93-
authors=[self.follow_npub],
100+
authors=[follow_npub_hex],
94101
)])
95102
print(f"DEBUG: Subscription filters: {self.filters.to_json_array()}")
96103
self.relay_manager.add_subscription(self.subscription_id, self.filters)
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
import unittest
2+
import _thread
3+
import time
4+
5+
from mpos import App, PackageManager
6+
import mpos.apps
7+
8+
from websocket import WebSocketApp
9+
10+
11+
# demo_multiple_ws.py
12+
import asyncio
13+
import aiohttp
14+
from aiohttp import WSMsgType
15+
import logging
16+
import sys
17+
from typing import List
18+
19+
20+
21+
# ----------------------------------------------------------------------
22+
# Logging
23+
# ----------------------------------------------------------------------
24+
logging.basicConfig(
25+
level=logging.INFO,
26+
format="%(asctime)s [%(levelname)s] %(message)s",
27+
stream=sys.stdout,
28+
)
29+
log = logging.getLogger(__name__)
30+
31+
32+
class TestTwoWebsockets(unittest.TestCase):
33+
34+
# ----------------------------------------------------------------------
35+
# Configuration
36+
# ----------------------------------------------------------------------
37+
# Change these to point to a real echo / chat server you control.
38+
WS_URLS = [
39+
"wss://echo.websocket.org", # public echo service (may be down)
40+
"wss://echo.websAcket.org", # duplicate on purpose – shows concurrency
41+
"wss://echo.websUcket.org",
42+
# add more URLs here…
43+
]
44+
45+
nr_connected = 0
46+
47+
# How many messages each connection should send before closing gracefully
48+
MESSAGES_PER_CONNECTION = 2
49+
STOP_AFTER = 10
50+
51+
# ----------------------------------------------------------------------
52+
# One connection worker
53+
# ----------------------------------------------------------------------
54+
async def ws_worker(self, session: aiohttp.ClientSession, url: str, idx: int) -> None:
55+
"""
56+
Handles a single WebSocket connection:
57+
* sends a few messages,
58+
* echoes back everything it receives,
59+
* closes when the remote end says "close" or after MESSAGES_PER_CONNECTION.
60+
"""
61+
try:
62+
async with session.ws_connect(url) as ws:
63+
log.info(f"[{idx}] Connected to {url}")
64+
self.nr_connected += 1
65+
66+
# ------------------------------------------------------------------
67+
# 1. Send a few starter messages
68+
# ------------------------------------------------------------------
69+
for i in range(self.MESSAGES_PER_CONNECTION):
70+
payload = f"Hello from client #{idx} – msg {i+1}"
71+
await ws.send_str(payload)
72+
log.info(f"[{idx}] → {payload}")
73+
74+
# give the server a moment to reply
75+
await asyncio.sleep(0.5)
76+
77+
# ------------------------------------------------------------------
78+
# 2. Echo-loop – react to incoming messages
79+
# ------------------------------------------------------------------
80+
msgcounter = 0
81+
async for msg in ws:
82+
msgcounter += 1
83+
if msgcounter > self.STOP_AFTER:
84+
print("Max reached, stopping...")
85+
await ws.close()
86+
break
87+
if msg.type == WSMsgType.TEXT:
88+
data: str = msg.data
89+
log.info(f"[{idx}] ← {data}")
90+
91+
# Echo back (with a suffix)
92+
reply = data + " / answer"
93+
await ws.send_str(reply)
94+
log.info(f"[{idx}] → {reply}")
95+
96+
# Close if server asks us to
97+
if data.strip().lower() == "close cmd":
98+
log.info(f"[{idx}] Server asked to close → closing")
99+
await ws.close()
100+
break
101+
102+
elif msg.type in (WSMsgType.CLOSE, WSMsgType.CLOSING, WSMsgType.CLOSED):
103+
log.info(f"[{idx}] Connection closed by remote")
104+
break
105+
106+
elif msg.type == WSMsgType.ERROR:
107+
log.error(f"[{idx}] WebSocket error: {ws.exception()}")
108+
break
109+
110+
except asyncio.CancelledError:
111+
log.info(f"[{idx}] Task cancelled")
112+
raise
113+
except Exception as exc:
114+
log.exception(f"[{idx}] Unexpected error on {url}: {exc}")
115+
finally:
116+
log.info(f"[{idx}] Worker finished for {url}")
117+
118+
# ----------------------------------------------------------------------
119+
# Main entry point – creates a single ClientSession + many tasks
120+
# ----------------------------------------------------------------------
121+
async def main(self) -> None:
122+
async with aiohttp.ClientSession() as session:
123+
# Create one task per URL (they all run concurrently)
124+
tasks = [
125+
asyncio.create_task(self.ws_worker(session, url, idx))
126+
for idx, url in enumerate(self.WS_URLS)
127+
]
128+
129+
log.info(f"Starting {len(tasks)} concurrent WebSocket connections…")
130+
# Wait for *all* of them to finish (or be cancelled)
131+
await asyncio.gather(*tasks, return_exceptions=True)
132+
log.info(f"All tasks stopped successfully!")
133+
self.assertTrue(self.nr_connected, len(self.WS_URLS))
134+
135+
def newthread(self):
136+
asyncio.run(self.main())
137+
138+
def test_it(self):
139+
_thread.stack_size(mpos.apps.good_stack_size())
140+
_thread.start_new_thread(self.newthread, ())
141+
time.sleep(10)
142+
143+
144+

0 commit comments

Comments
 (0)