Coverage for slidge / core / mixins / recipient.py: 100%
38 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-06-13 04:38 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-06-13 04:38 +0000
1import abc
3from slixmpp import JID
4from slixmpp.plugins.xep_0004.stanza.form import Form
6from ...util.types import ChatState, Sticker, XMPPMessage
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 @abc.abstractmethod
47 async def on_message(self, message: XMPPMessage) -> str | None:
48 """
49 Triggered when the user sends a message to this :term:`Recipient`.
50 """
51 raise NotImplementedError
53 @abc.abstractmethod
54 async def on_sticker(self, sticker: Sticker) -> str | None:
55 """
56 Triggered when the user sends a sticker to this :term:`Recipient`.
58 :param sticker: The sticker sent by the user.
60 :return: An ID of some sort that can be used later to ack and mark the
61 message as read by the user
62 """
63 raise NotImplementedError
65 async def on_chat_state(self, chat_state: ChatState, thread: str | None) -> None:
66 """
67 Triggered when the user sends a chat state (:xep:`0085`) to this
68 :term:`Recipient`.
70 :param chat_state: The sticker sent by the user.
71 """
72 raise NotImplementedError
74 async def on_displayed(self, legacy_msg_id: str, thread: str | None) -> None:
75 """
76 Triggered when the user sends a read marker (:xep:`0333`) to this
77 :term:`Recipient`.
79 This is only possible if a valid ``legacy_msg_id`` was passed when
80 transmitting a message from a legacy chat to the user, eg in
81 :meth:`slidge.contact.LegacyContact.send_text`
82 or
83 :meth:`slidge.group.LegacyParticipant.send_text`.
85 :param legacy_msg_id: Identifier of the message/
86 :param thread:
87 """
88 raise NotImplementedError
90 async def on_react(
91 self, legacy_msg_id: str, emojis: list[str], thread: str | None
92 ) -> None:
93 """
94 Triggered when the user sends an emoji reaction (:xep:`0444`) to
95 this :term:`Recipient`.
97 :param legacy_msg_id: ID of the message the user reacts to
98 :param emojis: Unicode characters representing reactions to the message ``legacy_msg_id``.
99 An empty list means "no reaction", ie, remove all reactions if any were present before
100 :param thread:
101 """
102 raise NotImplementedError
104 async def on_retract(self, legacy_msg_id: str, thread: str | None) -> None:
105 """
106 Triggered when the user retracts (:xep:`0424`) a message.
108 :param legacy_msg_id: Legacy ID of the retracted message
109 :param thread:
110 """
111 raise NotImplementedError
114class RecipientMixin(ReactionMixin, ThreadMixin, MessageMixin):
115 jid: JID
117 def __eq__(self, other: object) -> bool:
118 return isinstance(other, self.__class__) and self.jid == other.jid