Coverage for slidge / util / types.py: 94%
120 statements
« prev ^ index » next coverage.py v7.13.0, created at 2026-02-15 09:02 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2026-02-15 09:02 +0000
1"""
2Typing stuff
3"""
5from collections.abc import AsyncIterator, Hashable
6from dataclasses import dataclass, fields
7from datetime import datetime
8from enum import IntEnum
9from pathlib import Path
10from typing import (
11 IO,
12 TYPE_CHECKING,
13 Any,
14 Generic,
15 Literal,
16 NamedTuple,
17 TypedDict,
18 TypeVar,
19 Union,
20)
22from slixmpp import Message, Presence
23from slixmpp.types import PresenceShows, PresenceTypes, ResourceDict # noqa: F401
25if TYPE_CHECKING:
26 from ..contact import LegacyContact
27 from ..core.session import BaseSession
28 from ..group import LegacyMUC
29 from ..group.participant import LegacyParticipant
31 AnyBaseSession = BaseSession[Any, Any]
32else:
33 # Hack to work around circular import.
34 AnyBaseSession = int
37LegacyGroupIdType = TypeVar("LegacyGroupIdType", bound=Hashable)
38"""
39Type of the unique identifier for groups, usually a str or an int,
40but anything hashable should work.
41"""
42LegacyMessageType = TypeVar("LegacyMessageType", bound=Hashable)
43LegacyThreadType = TypeVar("LegacyThreadType", bound=Hashable)
44LegacyUserIdType = TypeVar("LegacyUserIdType", bound=Hashable)
46LegacyContactType = TypeVar("LegacyContactType", bound="LegacyContact[Any]")
47LegacyMUCType = TypeVar("LegacyMUCType", bound="LegacyMUC[Any, Any, Any, Any]")
48LegacyParticipantType = TypeVar("LegacyParticipantType", bound="LegacyParticipant")
50Recipient = Union["LegacyMUC[Any, Any, Any, Any]", "LegacyContact[Any]"]
51RecipientType = TypeVar("RecipientType", bound=Recipient)
52Sender = Union["LegacyContact[Any]", "LegacyParticipant"]
53LegacyFileIdType = int | str
55ChatState = Literal["active", "composing", "gone", "inactive", "paused"]
56ProcessingHint = Literal["no-store", "markable", "store"]
57Marker = Literal["acknowledged", "received", "displayed"]
58FieldType = Literal[
59 "boolean",
60 "fixed",
61 "text-single",
62 "jid-single",
63 "jid-multi",
64 "list-single",
65 "list-multi",
66 "text-private",
67]
68MucAffiliation = Literal["owner", "admin", "member", "outcast", "none"]
69MucRole = Literal["visitor", "participant", "moderator", "none"]
70# https://xmpp.org/registrar/disco-categories.html#client
71ClientType = Literal[
72 "bot", "console", "game", "handheld", "pc", "phone", "sms", "tablet", "web"
73]
74AttachmentDisposition = Literal["attachment", "inline"]
77@dataclass
78class MessageReference(Generic[LegacyMessageType]):
79 """
80 A "message reply", ie a "quoted message" (:xep:`0461`)
82 At the very minimum, the legacy message ID attribute must be set, but to
83 ensure that the quote is displayed in all XMPP clients, the author must also
84 be set (use the string "user" if the slidge user is the author of the referenced
85 message).
86 The body is used as a fallback for XMPP clients that do not support :xep:`0461`
87 of that failed to find the referenced message.
88 """
90 legacy_id: LegacyMessageType
91 author: Union[Literal["user"], "LegacyParticipant", "LegacyContact"] | None = None
92 body: str | None = None
95@dataclass
96class LegacyAttachment:
97 """
98 A file attachment to a message
100 At the minimum, one of the ``path``, ``steam``, ``data`` or ``url`` attribute
101 has to be set
103 To be used with :meth:`.LegacyContact.send_files` or
104 :meth:`.LegacyParticipant.send_files`
105 """
107 path: Path | str | None = None
108 name: str | None = None
109 stream: IO[bytes] | None = None
110 aio_stream: AsyncIterator[bytes] | None = None
111 data: bytes | None = None
112 content_type: str | None = None
113 legacy_file_id: str | int | None = None
114 url: str | None = None
115 caption: str | None = None
116 disposition: AttachmentDisposition | None = None
117 """
118 A caption for this specific image. For a global caption for a list of attachments,
119 use the ``body`` parameter of :meth:`.AttachmentMixin.send_files`
120 """
122 def __post_init__(self) -> None:
123 if all(
124 x is None
125 for x in (self.path, self.stream, self.data, self.url, self.aio_stream)
126 ):
127 raise TypeError("There is not data in this attachment", self)
128 if isinstance(self.path, str):
129 self.path = Path(self.path)
131 def format_for_user(self) -> str:
132 if self.name:
133 name = self.name
134 elif self.path:
135 name = self.path.name # type:ignore[union-attr]
136 elif self.url:
137 name = self.url
138 else:
139 name = ""
141 if self.caption:
142 if name:
143 name = f"{name}: {self.caption}"
144 else:
145 name = self.caption
147 return name
149 def __str__(self):
150 attrs = ", ".join(
151 f"{f.name}={getattr(self, f.name)!r}"
152 for f in fields(self)
153 if getattr(self, f.name) is not None and f.name != "data"
154 )
155 if self.data is not None:
156 data_str = f"data=<{len(self.data)} bytes>"
157 to_join = (attrs, data_str) if attrs else (data_str,)
158 attrs = ", ".join(to_join)
159 return f"Attachment({attrs})"
162class MucType(IntEnum):
163 """
164 The type of group, private, public, anonymous or not.
165 """
167 GROUP = 0
168 """
169 A private group, members-only and non-anonymous, eg a family group.
170 """
171 CHANNEL = 1
172 """
173 A public group, aka an anonymous channel.
174 """
175 CHANNEL_NON_ANONYMOUS = 2
176 """
177 A public group where participants' legacy IDs are visible to everybody.
178 """
181PseudoPresenceShow = PresenceShows | Literal[""]
184MessageOrPresenceTypeVar = TypeVar("MessageOrPresenceTypeVar", bound=Message | Presence)
187class LinkPreview(NamedTuple):
188 about: str
189 title: str | None
190 description: str | None
191 url: str | None
192 image: str | None
193 type: str | None
194 site_name: str | None
197class Mention(NamedTuple):
198 contact: "LegacyContact[Any]"
199 start: int
200 end: int
203class Hat(NamedTuple):
204 uri: str
205 title: str
206 hue: float | None = None
209class UserPreferences(TypedDict):
210 sync_avatar: bool
211 sync_presence: bool
214class MamMetadata(NamedTuple):
215 id: str
216 sent_on: datetime
219class HoleBound(NamedTuple):
220 id: int | str
221 timestamp: datetime
224class CachedPresence(NamedTuple):
225 last_seen: datetime | None = None
226 ptype: PresenceTypes | None = None
227 pstatus: str | None = None
228 pshow: PresenceShows | None = None
231class Sticker(NamedTuple):
232 path: Path
233 content_type: str | None
234 hashes: dict[str, str]
237class Avatar(NamedTuple):
238 path: Path | None = None
239 unique_id: str | int | None = None
240 url: str | None = None
241 data: bytes | None = None