56
56
)
57
57
from raiden .network .utils import get_average_http_response_time
58
58
from raiden .storage .serialization .serializer import MessageSerializer
59
+ from raiden .utils .capabilities import deserialize_capabilities , serialize_capabilities
59
60
from raiden .utils .gevent import spawn_named
60
61
from raiden .utils .signer import Signer , recover
61
- from raiden .utils .typing import Address , ChainID , MessageID , Signature
62
+ from raiden .utils .typing import Address , ChainID , MessageID , PeerCapabilities , Signature
62
63
from raiden_contracts .constants import ID_TO_CHAINNAME
63
64
64
65
log = structlog .get_logger (__name__ )
@@ -186,7 +187,9 @@ def __init__(
186
187
self ,
187
188
client : GMatrixClient ,
188
189
displayname_cache : DisplayNameCache ,
189
- address_reachability_changed_callback : Callable [[Address , AddressReachability ], None ],
190
+ address_reachability_changed_callback : Callable [
191
+ [Address , AddressReachability , PeerCapabilities ], None
192
+ ],
190
193
user_presence_changed_callback : Optional [Callable [[User , UserPresence ], None ]] = None ,
191
194
_log_context : Optional [Dict [str , Any ]] = None ,
192
195
) -> None :
@@ -269,6 +272,10 @@ def get_address_reachability_state(self, address: Address) -> ReachabilityState:
269
272
""" Return the current reachability state for ``address``. """
270
273
return self ._address_to_reachabilitystate .get (address , UNKNOWN_REACHABILITY_STATE )
271
274
275
+ def get_address_capabilities (self , address : Address ) -> PeerCapabilities :
276
+ """ Return the protocol capabilities for ``address``. """
277
+ return self ._address_to_capabilities .get (address , PeerCapabilities ({}))
278
+
272
279
def force_user_presence (self , user : User , presence : UserPresence ) -> None :
273
280
""" Forcibly set the ``user`` presence to ``presence``.
274
281
@@ -331,6 +338,18 @@ def track_address_presence(
331
338
332
339
self ._maybe_address_reachability_changed (address )
333
340
341
+ def query_capabilities_for_user_id (self , user_id : str ) -> PeerCapabilities :
342
+ """ This pulls the `avatar_url` for a given user/user_id and parses the capabilities. """
343
+ try :
344
+ user : User = self ._client .get_user (user_id )
345
+ except MatrixRequestError :
346
+ return PeerCapabilities ({})
347
+ avatar_url = user .get_avatar_url ()
348
+ if avatar_url is not None :
349
+ return PeerCapabilities (deserialize_capabilities (avatar_url ))
350
+ else :
351
+ return PeerCapabilities ({})
352
+
334
353
def get_reachability_from_matrix (self , user_ids : Iterable [str ]) -> AddressReachability :
335
354
""" Get the current reachability without any side effects
336
355
@@ -347,11 +366,14 @@ def get_reachability_from_matrix(self, user_ids: Iterable[str]) -> AddressReacha
347
366
def _maybe_address_reachability_changed (self , address : Address ) -> None :
348
367
# A Raiden node may have multiple Matrix users, this happens when
349
368
# Raiden roams from a Matrix server to another. This loop goes over all
350
- # these users and uses the "best" presence. IOW, if there is a single
369
+ # these users and uses the "best" presence. IOW, if there is at least one
351
370
# Matrix user that is reachable, then the Raiden node is considered
352
371
# reachable.
353
372
userids = self ._address_to_userids [address ].copy ()
354
- composite_presence = {self ._userid_to_presence .get (uid ) for uid in userids }
373
+ presence_to_uid = defaultdict (list )
374
+ for uid in userids :
375
+ presence_to_uid [self ._userid_to_presence .get (uid )].append (uid )
376
+ composite_presence = set (presence_to_uid .keys ())
355
377
356
378
new_presence = UserPresence .UNKNOWN
357
379
for presence in UserPresence .__members__ .values ():
@@ -364,7 +386,9 @@ def _maybe_address_reachability_changed(self, address: Address) -> None:
364
386
prev_reachability_state = self .get_address_reachability_state (address )
365
387
if new_address_reachability == prev_reachability_state .reachability :
366
388
return
367
-
389
+ # for capabilities, we get the "first" uid that showed the `new_presence`
390
+ present_uid = presence_to_uid [new_presence ].pop ()
391
+ capabilities = self .query_capabilities_for_user_id (present_uid )
368
392
now = datetime .now ()
369
393
370
394
self .log .debug (
@@ -379,8 +403,11 @@ def _maybe_address_reachability_changed(self, address: Address) -> None:
379
403
self ._address_to_reachabilitystate [address ] = ReachabilityState (
380
404
new_address_reachability , now
381
405
)
406
+ self ._address_to_capabilities [address ] = capabilities
382
407
383
- self ._address_reachability_changed_callback (address , new_address_reachability )
408
+ self ._address_reachability_changed_callback (
409
+ address , new_address_reachability , capabilities
410
+ )
384
411
385
412
def _presence_listener (self , event : Dict [str , Any ], presence_update_id : int ) -> None :
386
413
"""
@@ -437,6 +464,7 @@ def _presence_listener(self, event: Dict[str, Any], presence_update_id: int) ->
437
464
def _reset_state (self ) -> None :
438
465
self ._address_to_userids : Dict [Address , Set [str ]] = defaultdict (set )
439
466
self ._address_to_reachabilitystate : Dict [Address , ReachabilityState ] = dict ()
467
+ self ._address_to_capabilities : Dict [Address , PeerCapabilities ] = dict ()
440
468
self ._userid_to_presence : Dict [str , UserPresence ] = dict ()
441
469
self ._userid_to_presence_update_id : Dict [str , int ] = dict ()
442
470
@@ -569,7 +597,7 @@ def join_broadcast_room(client: GMatrixClient, broadcast_room_alias: str) -> Roo
569
597
)
570
598
571
599
572
- def first_login (client : GMatrixClient , signer : Signer , username : str ) -> User :
600
+ def first_login (client : GMatrixClient , signer : Signer , username : str , cap_str : str ) -> User :
573
601
"""Login within a server.
574
602
575
603
There are multiple cases where a previous auth token can become invalid and
@@ -635,6 +663,12 @@ def first_login(client: GMatrixClient, signer: Signer, username: str) -> User:
635
663
if current_display_name != signature_hex :
636
664
user .set_display_name (signature_hex )
637
665
666
+ current_capabilities = user .get_avatar_url () or ""
667
+
668
+ # Only set the capabilities if necessary.
669
+ if current_capabilities != cap_str :
670
+ user .set_avatar_url (cap_str )
671
+
638
672
log .debug (
639
673
"Logged in" ,
640
674
node = to_checksum_address (username ),
@@ -679,14 +713,21 @@ def login_with_token(client: GMatrixClient, user_id: str, access_token: str) ->
679
713
return client .get_user (client .user_id )
680
714
681
715
682
- def login (client : GMatrixClient , signer : Signer , prev_auth_data : Optional [str ] = None ) -> User :
683
- """ Login with a matrix server.
716
+ def login (
717
+ client : GMatrixClient ,
718
+ signer : Signer ,
719
+ prev_auth_data : Optional [str ] = None ,
720
+ capabilities : Dict [str , Any ] = None ,
721
+ ) -> User :
722
+ """Login with a matrix server.
684
723
685
724
Params:
686
725
client: GMatrixClient instance configured with desired homeserver.
687
726
signer: Signer used to sign the password and displayname.
688
727
prev_auth_data: Previously persisted authentication using the format "{user}/{password}".
689
728
"""
729
+ if capabilities is None :
730
+ capabilities = {}
690
731
server_url = client .api .base_url
691
732
server_name = urlparse (server_url ).netloc
692
733
@@ -708,7 +749,11 @@ def login(client: GMatrixClient, signer: Signer, prev_auth_data: Optional[str] =
708
749
server_name = server_name ,
709
750
)
710
751
711
- return first_login (client , signer , username )
752
+ try :
753
+ capstr = serialize_capabilities (capabilities )
754
+ except ValueError :
755
+ raise Exception ("error serializing" )
756
+ return first_login (client , signer , username , capstr )
712
757
713
758
714
759
@cached (cache = LRUCache (128 ), key = attrgetter ("user_id" , "displayname" ), lock = Semaphore ())
0 commit comments