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