Coverage for slidge/core/mixins/recipient.py: 97%
33 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-08-18 04:30 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-08-18 04:30 +0000
1import abc
3from slixmpp import JID
4from slixmpp.plugins.xep_0004.stanza.form import Form
6from ...util.types import ChatState
9class ReactionMixin:
10 REACTIONS_SINGLE_EMOJI = False
12 async def restricted_emoji_extended_feature(self) -> Form | None:
13 available = await self.available_emojis()
14 if not self.REACTIONS_SINGLE_EMOJI and available is None:
15 return None
17 form = Form()
18 form["type"] = "result"
19 form.add_field("FORM_TYPE", "hidden", value="urn:xmpp:reactions:0:restrictions")
20 if self.REACTIONS_SINGLE_EMOJI:
21 form.add_field("max_reactions_per_user", value="1", type="text-single")
22 if available:
23 form.add_field("allowlist", value=list(available), type="text-multi")
24 return form
26 @abc.abstractmethod
27 async def available_emojis(
28 self, legacy_msg_id: str | None = None
29 ) -> set[str] | None:
30 """
31 Override this to restrict the subset of reactions this recipient
32 can handle.
34 :return: A set of emojis or None if any emoji is allowed
35 """
36 return None
39class ThreadMixin:
40 @abc.abstractmethod
41 async def create_thread(self, xmpp_id: str) -> str:
42 return xmpp_id
45class MessageMixin:
46 async def on_chat_state(self, chat_state: ChatState, thread: str | None) -> None:
47 """
48 Triggered when the user sends a chat state (:xep:`0085`) to this
49 :term:`Recipient`.
51 :param chat_state: The sticker sent by the user.
52 """
53 raise NotImplementedError
55 async def on_displayed(self, legacy_msg_id: str, thread: str | None) -> None:
56 """
57 Triggered when the user sends a read marker (:xep:`0333`) to this
58 :term:`Recipient`.
60 This is only possible if a valid ``legacy_msg_id`` was passed when
61 transmitting a message from a legacy chat to the user, eg in
62 :meth:`slidge.contact.LegacyContact.send_text`
63 or
64 :meth:`slidge.group.LegacyParticipant.send_text`.
66 :param legacy_msg_id: Identifier of the message/
67 :param thread:
68 """
69 raise NotImplementedError
71 async def on_react(
72 self, legacy_msg_id: str, emojis: list[str], thread: str | None
73 ) -> None:
74 """
75 Triggered when the user sends an emoji reaction (:xep:`0444`) to
76 this :term:`Recipient`.
78 :param legacy_msg_id: ID of the message the user reacts to
79 :param emojis: Unicode characters representing reactions to the message ``legacy_msg_id``.
80 An empty list means "no reaction", ie, remove all reactions if any were present before
81 :param thread:
82 """
83 raise NotImplementedError
85 async def on_retract(self, legacy_msg_id: str, thread: str | None) -> None:
86 """
87 Triggered when the user retracts (:xep:`0424`) a message.
89 :param legacy_msg_id: Legacy ID of the retracted message
90 :param thread:
91 """
92 raise NotImplementedError
95class RecipientMixin(ReactionMixin, ThreadMixin, MessageMixin):
96 jid: JID
97 is_group: bool
99 def __eq__(self, other: object) -> bool:
100 return isinstance(other, self.__class__) and self.jid == other.jid