Coverage for slidge/core/dispatcher/muc/misc.py: 92%
66 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-05-04 08:17 +0000
« prev ^ index » next coverage.py v7.8.0, created at 2025-05-04 08:17 +0000
1import logging
3from slixmpp import JID, CoroutineCallback, Iq, Message, Presence, StanzaPath
4from slixmpp.exceptions import XMPPError
6from ..util import DispatcherMixin, exceptions_to_xmpp_errors
9class MucMiscMixin(DispatcherMixin):
10 __slots__: list[str] = []
12 def __init__(self, xmpp) -> None:
13 super().__init__(xmpp)
14 xmpp.register_handler(
15 CoroutineCallback(
16 "ibr_remove", StanzaPath("/iq/register"), self.on_ibr_remove
17 )
18 )
20 xmpp.add_event_handler("groupchat_join", self.on_groupchat_join)
21 xmpp.add_event_handler(
22 "groupchat_direct_invite", self.on_groupchat_direct_invite
23 )
24 xmpp.add_event_handler("groupchat_subject", self.on_groupchat_subject)
25 xmpp.add_event_handler("groupchat_message_error", self.__on_group_chat_error)
27 async def __on_group_chat_error(self, msg: Message) -> None:
28 condition = msg["error"].get_condition()
29 if condition not in KICKABLE_ERRORS:
30 return
32 try:
33 muc = await self.get_muc_from_stanza(msg)
34 except XMPPError as e:
35 log.debug("Not removing resource", exc_info=e)
36 return
37 mfrom = msg.get_from()
38 resource = mfrom.resource
39 try:
40 muc.remove_user_resource(resource)
41 except KeyError:
42 # this actually happens quite frequently on for both beagle and monal
43 # (not sure why?), but is of no consequence
44 log.debug("%s was not in the resources of %s", resource, muc)
45 else:
46 log.info(
47 "Removed %s from the resources of %s because of error", resource, muc
48 )
50 @exceptions_to_xmpp_errors
51 async def on_ibr_remove(self, iq: Iq):
52 if iq.get_to() == self.xmpp.boundjid.bare:
53 return
55 if iq["type"] == "set" and iq["register"]["remove"]:
56 muc = await self.get_muc_from_stanza(iq)
57 await muc.session.on_leave_group(muc.legacy_id)
58 iq.reply().send()
59 await muc.session.bookmarks.remove(
60 muc, "You left this chat from an XMPP client."
61 )
62 return
64 raise XMPPError("feature-not-implemented")
66 @exceptions_to_xmpp_errors
67 async def on_groupchat_join(self, p: Presence):
68 if not self.xmpp.GROUPS:
69 raise XMPPError(
70 "feature-not-implemented",
71 "This gateway does not implement multi-user chats.",
72 )
73 muc = await self.get_muc_from_stanza(p)
74 await muc.join(p)
76 @exceptions_to_xmpp_errors
77 async def on_groupchat_direct_invite(self, msg: Message):
78 invite = msg["groupchat_invite"]
79 jid = JID(invite["jid"])
81 if jid.domain != self.xmpp.boundjid.bare:
82 raise XMPPError(
83 "bad-request",
84 "Legacy contacts can only be invited to legacy groups, not standard XMPP MUCs.",
85 )
87 if invite["password"]:
88 raise XMPPError(
89 "bad-request", "Password-protected groups are not supported"
90 )
92 session = await self._get_session(msg, logged=True)
93 contact = await session.contacts.by_jid(msg.get_to())
94 muc = await session.bookmarks.by_jid(jid)
96 await session.on_invitation(contact, muc, invite["reason"] or None)
98 @exceptions_to_xmpp_errors
99 async def on_groupchat_subject(self, msg: Message):
100 muc = await self.get_muc_from_stanza(msg)
101 if not muc.HAS_SUBJECT:
102 raise XMPPError(
103 "bad-request",
104 "There are no room subject in here. "
105 "Use the room configuration to update its name or description",
106 )
107 await muc.on_set_subject(msg["subject"])
110KICKABLE_ERRORS = {
111 "gone",
112 "internal-server-error",
113 "item-not-found",
114 "jid-malformed",
115 "recipient-unavailable",
116 "redirect",
117 "remote-server-not-found",
118 "remote-server-timeout",
119 "service-unavailable",
120 "malformed error",
121}
123log = logging.getLogger(__name__)