@@ -28,9 +28,13 @@ def __str__(self):
2828 elif isinstance (self , NWCWallet ):
2929 return "NWCWallet"
3030
31- def start_refresh_balance (self ):
31+ def start_refresh_balance (self , balance_updated_cb ):
3232 _thread .stack_size (mpos .apps .good_stack_size ())
33- _thread .start_new_thread (self .fetch_balance_thread , ())
33+ _thread .start_new_thread (self .fetch_balance_thread , (balance_updated_cb ,))
34+
35+ def destroy (self ):
36+ # optional to inherit
37+ pass
3438
3539
3640class LNBitsWallet (Wallet ):
@@ -40,7 +44,7 @@ def __init__(self, lnbits_url, lnbits_readkey):
4044 self .lnbits_url = lnbits_url
4145 self .lnbits_readkey = lnbits_readkey
4246
43- def fetch_balance_thread (self ):
47+ def fetch_balance_thread (self , balance_updated_cb ):
4448 print ("fetch_balance_thread" )
4549 walleturl = self .lnbits_url + "/api/v1/wallet"
4650 headers = {
@@ -61,6 +65,7 @@ def fetch_balance_thread(self):
6165 balance_msat = balance_reply ['balance' ]
6266 self .last_known_balance = round (balance_msat / 1000 )
6367 #self.last_known_balance_timestamp = mpos.time.epoch_seconds()
68+ balance_updated_cb ()
6469 except Exception as e :
6570 print (f"Could not parse reponse text '{ response_text } ' as JSON: { e } " )
6671
@@ -74,49 +79,97 @@ def __init__(self, nwc_url):
7479 self .private_key = PrivateKey (bytes .fromhex (self .secret ))
7580 self .relay_manager = RelayManager ()
7681 self .relay_manager .add_relay (self .relay )
77- print (f"DEBUG: Opening relay connections" )
78- self .relay_manager .open_connections ({"cert_reqs" : ssl .CERT_NONE })
82+ self .connected = False
83+
84+ def destroy (self ):
85+ self .relay_manager .close_connections ()
86+
87+ def fetch_balance_thread (self , balance_updated_cb ) :
88+ # make sure connected to relay (otherwise connect)
89+ if self .relay_manager .relays [self .relay ].connected != True :
90+ print (f"DEBUG: Opening relay connections" )
91+ self .relay_manager .open_connections ({"cert_reqs" : ssl .CERT_NONE })
92+ self .connected = False
93+ for _ in range (20 ):
94+ time .sleep (0.5 )
95+ if self .relay_manager .relays [self .relay ].connected is True :
96+ self .connected = True
97+ break
98+ print ("Waiting for relay connection..." )
99+ if not self .connected :
100+ print ("Failed to connect, aborting..." )
101+ return
79102 # Set up subscription to receive response
80103 self .subscription_id = "nwc_balance_" + str (round (time .time ()))
81- # print(f"DEBUG: Setting up subscription with ID: {self.subscription_id}")
104+ print (f"DEBUG: Setting up subscription with ID: { self .subscription_id } " )
82105 self .filters = Filters ([Filter (
83106 kinds = [23195 ], # NWC replies
84107 authors = [self .wallet_pubkey ],
85108 pubkey_refs = [self .private_key .public_key .hex ()]
86109 )])
87- # print(f"DEBUG: Subscription filters: {filters.to_json_array()}")
110+ print (f"DEBUG: Subscription filters: { self . filters .to_json_array ()} " )
88111 self .relay_manager .add_subscription (self .subscription_id , self .filters )
89- time .sleep (5 )
90-
91-
92- def start_refresh_balance (self ) :
93- # TODO: make sure connected to relay (otherwise connect)
94-
112+ time .sleep (1 )
95113 # Create get_balance request
96114 balance_request = {
97115 "method" : "get_balance" ,
98116 "params" : {}
99117 }
100118 print (f"DEBUG: Created balance request: { balance_request } " )
101-
102119 print (f"DEBUG: Creating encrypted DM to wallet pubkey: { self .wallet_pubkey } " )
103120 dm = EncryptedDirectMessage (
104121 recipient_pubkey = self .wallet_pubkey ,
105122 cleartext_content = json .dumps (balance_request )
106123 )
107-
108124 print (f"DEBUG: Signing DM { json .dumps (dm )} with private key" )
109125 self .private_key .sign_event (dm ) # sign also does encryption if it's a encrypted dm
110126 print (f"DEBUG: DM created with ID: { dm .id } " )
111-
112127 # Publish request
113128 print (f"DEBUG: Publishing subscription request" )
114129 request_message = [ClientMessageType .REQUEST , self .subscription_id ]
115130 request_message .extend (self .filters .to_json_array ())
116131 self .relay_manager .publish_message (json .dumps (request_message ))
117132 print (f"DEBUG: Publishing encrypted DM" )
118133 self .relay_manager .publish_event (dm )
119-
134+ # only accept events after the time it was published
135+ after_time = mpos .time .epoch_seconds ()
136+ after_time -= 60 # go back a bit because server clocks might be drifting
137+ print (f"will only consider events after { after_time } " )
138+
139+ # Wait for response
140+ print (f"DEBUG: Waiting for response..." )
141+ print (f"starting at { time .localtime ()} " )
142+ start_time = mpos .time .epoch_seconds ()
143+ balance = None
144+ while mpos .time .epoch_seconds () - start_time < 60 * 2 :
145+ while self .relay_manager .message_pool .has_events ():
146+ print (f"DEBUG: Event received from message pool" )
147+ event_msg = self .relay_manager .message_pool .get_event ()
148+ event_created_at = event_msg .event .created_at
149+ print (f"Received at { time .localtime ()} a message with timestamp { event_created_at } " )
150+ #if event_created_at < after_time:
151+ # print("Skipping event because it's too old!")
152+ # continue
153+ #print(f"event_msg content {event_msg.event.content}")
154+ try :
155+ #print(f"DEBUG: Decrypting event from public_key: {event_msg.event.public_key}")
156+ decrypted_content = self .private_key .decrypt_message (
157+ event_msg .event .content ,
158+ event_msg .event .public_key
159+ )
160+ print (f"DEBUG: Decrypted content: { decrypted_content } " )
161+ response = json .loads (decrypted_content )
162+ print (f"DEBUG: Parsed response: { response } " )
163+ self .last_known_balance = round (int (response ["result" ]["balance" ]) / 1000 )
164+ print (f"Got balance: { self .last_known_balance } " )
165+ balance_updated_cb ()
166+ break
167+ except Exception as e :
168+ print (f"DEBUG: Error processing response: { e } " )
169+ if balance is not None :
170+ break
171+ time .sleep (1 )
172+
120173 def parse_nwc_url (self , nwc_url ):
121174 """Parse Nostr Wallet Connect URL to extract pubkey, relay, secret, and lud16."""
122175 print (f"DEBUG: Starting to parse NWC URL: { nwc_url } " )
@@ -131,18 +184,14 @@ def parse_nwc_url(self, nwc_url):
131184 else :
132185 print (f"DEBUG: No recognized prefix found in URL" )
133186 raise ValueError ("Invalid NWC URL: missing 'nostr+walletconnect://' or 'nwc:' prefix" )
134-
135187 print (f"DEBUG: URL after prefix removal: { nwc_url } " )
136-
137188 # Split into pubkey and query params
138189 parts = nwc_url .split ('?' )
139190 pubkey = parts [0 ]
140191 print (f"DEBUG: Extracted pubkey: { pubkey } " )
141-
142192 # Validate pubkey (should be 64 hex characters)
143193 if len (pubkey ) != 64 or not all (c in '0123456789abcdef' for c in pubkey ):
144194 raise ValueError ("Invalid NWC URL: pubkey must be 64 hex characters" )
145-
146195 # Extract relay, secret, and lud16 from query params
147196 relay = None
148197 secret = None
@@ -162,14 +211,11 @@ def parse_nwc_url(self, nwc_url):
162211 print (f"DEBUG: Extracted lud16: { lud16 } " )
163212 else :
164213 print (f"DEBUG: No query parameters found" )
165-
166214 if not pubkey or not relay or not secret :
167215 raise ValueError ("Invalid NWC URL: missing required fields (pubkey, relay, or secret)" )
168-
169216 # Validate secret (should be 64 hex characters)
170217 if len (secret ) != 64 or not all (c in '0123456789abcdef' for c in secret ):
171218 raise ValueError ("Invalid NWC URL: secret must be 64 hex characters" )
172-
173219 print (f"DEBUG: Parsed NWC data - Relay: { relay } , Pubkey: { pubkey } , Secret: { secret } , lud16: { lud16 } " )
174220 return relay , pubkey , secret , lud16
175221 except Exception as e :
0 commit comments