Coverage for slidge / core / dispatcher / muc / admin.py: 93%
55 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-06 05:07 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-06 05:07 +0000
1import typing
3from slixmpp import JID, CoroutineCallback, Iq, StanzaPath
4from slixmpp.exceptions import XMPPError
5from slixmpp.xmlstream import StanzaBase
7from ..util import DispatcherMixin, exceptions_to_xmpp_errors
9if typing.TYPE_CHECKING:
10 from slidge.core.gateway import BaseGateway
13class MucAdminMixin(DispatcherMixin):
14 __slots__: list[str] = []
16 def __init__(self, xmpp: "BaseGateway") -> None:
17 super().__init__(xmpp)
18 self.xmpp.register_handler(
19 CoroutineCallback(
20 "MUCModerate",
21 StanzaPath("iq/moderate"),
22 self.on_user_moderation,
23 )
24 )
25 self.xmpp.register_handler(
26 CoroutineCallback(
27 "MUCSetAffiliation",
28 StanzaPath("iq@type=set/mucadmin_query"),
29 self.on_user_set_affiliation,
30 )
31 )
32 self.xmpp.register_handler(
33 CoroutineCallback(
34 "MUCGetAffiliation",
35 StanzaPath("iq@type=get/mucadmin_query"),
36 self.on_muc_admin_query_get,
37 )
38 )
40 @exceptions_to_xmpp_errors
41 async def on_user_moderation(self, iq: StanzaBase) -> None:
42 assert isinstance(iq, Iq)
43 muc = await self.get_muc_from_stanza(iq)
45 moderate = iq["moderate"]
46 xmpp_id = iq["moderate"]["id"]
47 if not xmpp_id:
48 raise XMPPError("bad-request", "Missing moderated message ID")
50 if not moderate["retract"]:
51 raise XMPPError(
52 "feature-not-implemented",
53 "Slidge only implements moderation/retraction",
54 )
56 legacy_id = self._xmpp_msg_id_to_legacy(muc.session, xmpp_id, muc)
57 await muc.session.on_moderate(muc, legacy_id, moderate["reason"] or None)
58 iq.reply(clear=True).send()
60 @exceptions_to_xmpp_errors
61 async def on_user_set_affiliation(self, iq: StanzaBase) -> None:
62 assert isinstance(iq, Iq)
63 muc = await self.get_muc_from_stanza(iq)
65 item = iq["mucadmin_query"]["item"]
66 if item["jid"]:
67 contact = await muc.session.contacts.by_jid(JID(item["jid"]))
68 else:
69 part = await muc.get_participant(
70 item["nick"], fill_first=True, create=False
71 )
72 if part is None:
73 raise XMPPError("item-not-found")
74 assert part.contact is not None
75 contact = part.contact
77 if item["affiliation"]:
78 await muc.on_set_affiliation(
79 contact,
80 item["affiliation"],
81 item["reason"] or None,
82 item["nick"] or None,
83 )
84 elif item["role"] == "none":
85 await muc.on_kick(contact, item["reason"] or None)
87 iq.reply(clear=True).send()
89 @exceptions_to_xmpp_errors
90 async def on_muc_admin_query_get(self, iq: StanzaBase) -> None:
91 assert isinstance(iq, Iq)
92 affiliation = iq["mucadmin_query"]["item"]["affiliation"]
94 if not affiliation:
95 raise XMPPError("bad-request")
97 session = await self._get_session(iq, 1, logged=True)
98 muc = await session.bookmarks.by_jid(iq.get_to())
100 reply = iq.reply()
101 reply.enable("mucadmin_query")
102 async for participant in muc.get_participants(affiliation):
103 reply["mucadmin_query"].append(participant.mucadmin_item())
104 reply.send()