1212
1313import mpos .apps
1414import mpos .time
15+ import mpos .util
1516
1617class Wallet :
1718
1819 # These values could be loading from a cache.json file at __init__
19- last_known_balance = 0
20+ last_known_balance = - 1
2021 #last_known_balance_timestamp = 0
22+ payment_list = []
2123
2224 def __init__ (self ):
2325 self .keep_running = True
@@ -28,16 +30,33 @@ def __str__(self):
2830 elif isinstance (self , NWCWallet ):
2931 return "NWCWallet"
3032
33+ def are_payment_lists_equal (self , list1 , list2 ):
34+ if len (list1 ) != len (list2 ):
35+ return False
36+ return all (p1 == p2 for p1 , p2 in zip (list1 , list2 ))
37+
38+ def handle_new_payments (self , new_payments ):
39+ print ("handle_new_payments" )
40+ if not self .are_payment_lists_equal (self .payment_list , new_payments ):
41+ print ("new list of payments" )
42+ self .payment_list = new_payments
43+ self .payments_updated_cb ()
44+
45+ def payment_list_string (self ):
46+ return "\n " .join (f"{ payment .amount_sats } sats: { payment .comment } " for payment in self .payment_list )
47+
3148 # Need callbacks for:
3249 # - started (so the user can show the UI)
3350 # - stopped (so the user can delete/free it)
3451 # - error (so the user can show the error)
3552 # - balance
3653 # - transactions
37- def start (self , balance_updated_cb ):
54+ def start (self , balance_updated_cb , payments_updated_cb ):
3855 self .keep_running = True
56+ self .balance_updated_cb = balance_updated_cb
57+ self .payments_updated_cb = payments_updated_cb
3958 _thread .stack_size (mpos .apps .good_stack_size ())
40- _thread .start_new_thread (self .wallet_manager_thread , (balance_updated_cb , ))
59+ _thread .start_new_thread (self .wallet_manager_thread , ())
4160
4261 def stop (self ):
4362 self .keep_running = False
@@ -52,20 +71,23 @@ def __init__(self, lnbits_url, lnbits_readkey):
5271 self .lnbits_url = lnbits_url
5372 self .lnbits_readkey = lnbits_readkey
5473
55- def wallet_manager_thread (self , balance_updated_cb ):
74+ def wallet_manager_thread (self ):
5675 print ("wallet_manager_thread" )
5776 while self .keep_running :
5877 try :
59- self .last_known_balance = fetch_balance ()
60- balance_updated_cb ()
61- # TODO: if the balance changed, then re-list transactions
78+ new_balance = self .fetch_balance ()
79+ if new_balance != self .last_known_balance :
80+ self .last_known_balance = new_balance
81+ self .balance_updated_cb ()
82+ new_payments = self .fetch_payments () # if the balance changed, then re-list transactions
83+ self .handle_new_payments (new_payments )
6284 except Exception as e :
63- print (f"WARNING: fetch_balance got exception { e } , ignorning." )
85+ print (f"WARNING: wallet_manager_thread got exception { e } , ignorning." )
6486 print ("Sleeping a while before re-fetching balance..." )
6587 time .sleep (60 )
6688 print ("wallet_manager_thread stopping" )
6789
68- def fetch_balance ():
90+ def fetch_balance (self ):
6991 walleturl = self .lnbits_url + "/api/v1/wallet"
7092 headers = {
7193 "X-Api-Key" : self .lnbits_readkey ,
@@ -87,6 +109,39 @@ def fetch_balance():
87109 print (f"Could not parse reponse text '{ response_text } ' as JSON: { e } " )
88110 raise e
89111
112+ def fetch_payments (self ):
113+ paymentsurl = self .lnbits_url + "/api/v1/payments?limit=6"
114+ headers = {
115+ "X-Api-Key" : self .lnbits_readkey ,
116+ }
117+ try :
118+ response = requests .get (paymentsurl , timeout = 10 , headers = headers )
119+ except Exception as e :
120+ print ("fetch_payments: get request failed:" , e )
121+ if response and response .status_code == 200 :
122+ response_text = response .text
123+ print (f"Got response text: { response_text } " )
124+ response .close ()
125+ try :
126+ payments_reply = json .loads (response_text )
127+ print (f"Got payments: { payments_reply } " )
128+ new_payments = []
129+ for payment in payments_reply :
130+ print (f"Got payment: { payment } " )
131+ amount = payment ["amount" ]
132+ amount = round (amount / 1000 )
133+ comment = payment ["memo" ]
134+ extra = payment ["extra" ]
135+ if extra :
136+ extracomment = extra ["comment" ]
137+ if extracomment :
138+ comment = extracomment
139+ payment = Payment (amount , comment )
140+ new_payments .append (payment )
141+ return new_payments
142+ except Exception as e :
143+ print (f"Could not parse reponse text '{ response_text } ' as JSON: { e } " )
144+ raise e
90145
91146class NWCWallet (Wallet ):
92147
@@ -95,7 +150,7 @@ def __init__(self, nwc_url):
95150 self .nwc_url = nwc_url
96151 self .connected = False
97152
98- def wallet_manager_thread (self , balance_updated_cb ):
153+ def wallet_manager_thread (self ):
99154 self .relay , self .wallet_pubkey , self .secret , self .lud16 = self .parse_nwc_url (self .nwc_url )
100155 self .private_key = PrivateKey (bytes .fromhex (self .secret ))
101156 self .relay_manager = RelayManager ()
@@ -167,7 +222,7 @@ def wallet_manager_thread(self, balance_updated_cb):
167222 self .last_known_balance = round (int (response ["result" ]["balance" ]) / 1000 )
168223 print (f"Got balance: { self .last_known_balance } " )
169224 # TODO: if balance changed, then update list of transactions
170- balance_updated_cb ()
225+ self . balance_updated_cb ()
171226 elif response ["result" ]["transactions" ]:
172227 print ("TODO: Response contains transactions!" )
173228 else :
@@ -197,7 +252,9 @@ def parse_nwc_url(self, nwc_url):
197252 print (f"DEBUG: No recognized prefix found in URL" )
198253 raise ValueError ("Invalid NWC URL: missing 'nostr+walletconnect://' or 'nwc:' prefix" )
199254 print (f"DEBUG: URL after prefix removal: { nwc_url } " )
200- # TODO: urldecode because the relay might have %3A%2F%2F etc
255+ # urldecode because the relay might have %3A%2F%2F etc
256+ nwc_url = mpos .util .urldecode (nwc_url )
257+ print (f"after urldecode: { nwc_url } " )
201258 # Split into pubkey and query params
202259 parts = nwc_url .split ('?' )
203260 pubkey = parts [0 ]
@@ -234,3 +291,17 @@ def parse_nwc_url(self, nwc_url):
234291 except Exception as e :
235292 print (f"DEBUG: Error parsing NWC URL: { e } " )
236293
294+
295+ class Payment :
296+
297+ def __init__ (self , amount_sats , comment ):
298+ self .amount_sats = amount_sats
299+ self .comment = comment
300+
301+ def __str__ (self ):
302+ return f"Payment(amount_sats={ self .amount_sats } , comment='{ self .comment } ')"
303+
304+ def __eq__ (self , other ):
305+ if not isinstance (other , Payment ):
306+ return False
307+ return self .amount_sats == other .amount_sats and self .comment == other .comment
0 commit comments