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
« 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
4from slixmpp import Message
5from slixmpp.exceptions import IqError, IqTimeout
6from slixmpp.plugins.xep_0084.stanza import Info
8from slidge.util.types import AnySession
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
21if TYPE_CHECKING:
22 from slidge.core.gateway import BaseGateway
24GatewayType = TypeVar("GatewayType", bound="BaseGateway[Any]")
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
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 )
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"]
56 await self.on_avatar_metadata_info(session, info)
58 async def on_avatar_metadata_info(self, session: AnySession, info: Info) -> None:
59 hash_ = info["id"]
61 if session.user.avatar_hash == hash_:
62 session.log.debug("We already know this avatar hash")
63 return
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)
104log = logging.getLogger(__name__)