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

1""" 

2Typing stuff 

3""" 

4 

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) 

23 

24from slixmpp import Message, Presence 

25from slixmpp.types import PresenceShows, PresenceTypes, ResourceDict 

26 

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 

32 

33 AnyBaseSession = BaseSession[Any, Any] 

34else: 

35 AnyBaseSession = None 

36 

37 

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) 

46 

47LegacyContactType = TypeVar("LegacyContactType", bound="LegacyContact[Any]") 

48LegacyMUCType = TypeVar("LegacyMUCType", bound="LegacyMUC[Any, Any, Any, Any]") 

49LegacyParticipantType = TypeVar("LegacyParticipantType", bound="LegacyParticipant") 

50 

51Recipient = Union["LegacyMUC[Any, Any, Any, Any]", "LegacyContact[Any]"] 

52RecipientType = TypeVar("RecipientType", bound=Recipient) 

53Sender = Union["LegacyContact[Any]", "LegacyParticipant"] 

54LegacyFileIdType = Union[int, str] 

55 

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"] 

76 

77 

78@dataclass 

79class MessageReference(Generic[LegacyMessageType]): 

80 """ 

81 A "message reply", ie a "quoted message" (:xep:`0461`) 

82 

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 """ 

90 

91 legacy_id: LegacyMessageType 

92 author: Optional[Union[Literal["user"], "LegacyParticipant", "LegacyContact"]] = ( 

93 None 

94 ) 

95 body: Optional[str] = None 

96 

97 

98@dataclass 

99class LegacyAttachment: 

100 """ 

101 A file attachment to a message 

102 

103 At the minimum, one of the ``path``, ``steam``, ``data`` or ``url`` attribute 

104 has to be set 

105 

106 To be used with :meth:`.LegacyContact.send_files` or 

107 :meth:`.LegacyParticipant.send_files` 

108 """ 

109 

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 """ 

124 

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) 

133 

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 = "" 

143 

144 if self.caption: 

145 if name: 

146 name = f"{name}: {self.caption}" 

147 else: 

148 name = self.caption 

149 

150 return name 

151 

152 

153class MucType(IntEnum): 

154 """ 

155 The type of group, private, public, anonymous or not. 

156 """ 

157 

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 """ 

170 

171 

172PseudoPresenceShow = Union[PresenceShows, Literal[""]] 

173 

174 

175MessageOrPresenceTypeVar = TypeVar( 

176 "MessageOrPresenceTypeVar", bound=Union[Message, Presence] 

177) 

178 

179 

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] 

188 

189 

190class Mention(NamedTuple): 

191 contact: "LegacyContact[Any]" 

192 start: int 

193 end: int 

194 

195 

196class Hat(NamedTuple): 

197 uri: str 

198 title: str 

199 hue: float | None = None 

200 

201 

202class UserPreferences(TypedDict): 

203 sync_avatar: bool 

204 sync_presence: bool 

205 

206 

207class MamMetadata(NamedTuple): 

208 id: str 

209 sent_on: datetime 

210 

211 

212class HoleBound(NamedTuple): 

213 id: int | str 

214 timestamp: datetime 

215 

216 

217class CachedPresence(NamedTuple): 

218 last_seen: Optional[datetime] = None 

219 ptype: Optional[PresenceTypes] = None 

220 pstatus: Optional[str] = None 

221 pshow: Optional[PresenceShows] = None 

222 

223 

224class Sticker(NamedTuple): 

225 path: Path 

226 content_type: Optional[str] 

227 hashes: dict[str, str] 

228 

229 

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