Coverage for slidge / core / dispatcher / session_dispatcher.py: 86%

65 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-05-20 19:56 +0000

1import logging 

2from typing import TYPE_CHECKING, Any, Generic, TypeVar 

3 

4from slixmpp import Message 

5from slixmpp.exceptions import IqError, IqTimeout 

6from slixmpp.plugins.xep_0084.stanza import Info 

7 

8from slidge.util.types import AnySession 

9 

10from .caps import CapsMixin 

11from .disco import DiscoMixin 

12from .message import MessageMixin 

13from .muc import MucMixin 

14from .presence import PresenceHandlerMixin 

15from .pubsub import PubSubMixin 

16from .registration import RegistrationMixin 

17from .search import SearchMixin 

18from .util import exceptions_to_xmpp_errors 

19from .vcard import VCardMixin 

20 

21if TYPE_CHECKING: 

22 from slidge.core.gateway import BaseGateway 

23 

24GatewayType = TypeVar("GatewayType", bound="BaseGateway[Any]") 

25 

26 

27class SessionDispatcher( 

28 CapsMixin, 

29 DiscoMixin, 

30 RegistrationMixin, 

31 MessageMixin, 

32 MucMixin, 

33 PresenceHandlerMixin, 

34 PubSubMixin, 

35 SearchMixin, 

36 VCardMixin, 

37 Generic[GatewayType], 

38): 

39 __slots__: list[str] = ["_MamMixin__mam_cleanup_task", "xmpp"] 

40 xmpp: GatewayType 

41 

42 def __init__(self, xmpp: GatewayType) -> None: 

43 super().__init__(xmpp) 

44 xmpp.add_event_handler( 

45 "avatar_metadata_publish", self.on_avatar_metadata_publish 

46 ) 

47 

48 @exceptions_to_xmpp_errors 

49 async def on_avatar_metadata_publish(self, m: Message) -> None: 

50 session = await self._get_session(m, timeout=None) 

51 if not session.user.preferences.get("sync_avatar", False): 

52 session.log.debug("User does not want to sync their avatar") 

53 return 

54 info = m["pubsub_event"]["items"]["item"]["avatar_metadata"]["info"] 

55 

56 await self.on_avatar_metadata_info(session, info) 

57 

58 async def on_avatar_metadata_info(self, session: AnySession, info: Info) -> None: 

59 hash_ = info["id"] 

60 

61 if session.user.avatar_hash == hash_: 

62 session.log.debug("We already know this avatar hash") 

63 return 

64 

65 if hash_: 

66 try: 

67 iq = await self.xmpp.plugin["xep_0084"].retrieve_avatar( 

68 session.user_jid, hash_, ifrom=self.xmpp.boundjid.bare 

69 ) 

70 except (IqError, IqTimeout) as e: 

71 session.log.warning("Could not fetch the user's avatar: %s", e) 

72 return 

73 bytes_ = iq["pubsub"]["items"]["item"]["avatar_data"]["value"] 

74 type_ = info["type"] 

75 height = info["height"] 

76 width = info["width"] 

77 else: 

78 with self.xmpp.store.session(expire_on_commit=False) as orm: 

79 session.user.avatar_hash = None 

80 orm.add(session.user) 

81 orm.commit() 

82 bytes_ = type_ = height = width = hash_ = None 

83 try: 

84 await session.on_avatar(bytes_, hash_, type_, width, height) 

85 except NotImplementedError: 

86 pass 

87 except Exception as e: 

88 # If something goes wrong here, replying an error stanza will to the 

89 # avatar update will likely not show in most clients, so let's send 

90 # a normal message from the component to the user. 

91 session.send_gateway_message( 

92 f"Something went wrong trying to set your avatar: {e!r}" 

93 ) 

94 else: 

95 session.user.avatar_hash = hash_ 

96 with self.xmpp.store.session(expire_on_commit=False) as orm: 

97 orm.add(session.user) 

98 orm.commit() 

99 for room in session.bookmarks: 

100 participant = await room.get_user_participant() 

101 participant.send_last_presence(force=True, no_cache_online=True) 

102 

103 

104log = logging.getLogger(__name__)