slidge 0.1.0rc1__py3-none-any.whl → 0.1.2__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (164) hide show
  1. slidge/__init__.py +54 -31
  2. slidge/__main__.py +51 -5
  3. slidge/command/__init__.py +28 -0
  4. slidge/command/adhoc.py +258 -0
  5. slidge/command/admin.py +193 -0
  6. slidge/command/base.py +441 -0
  7. slidge/command/categories.py +3 -0
  8. slidge/command/chat_command.py +288 -0
  9. slidge/command/register.py +179 -0
  10. slidge/command/user.py +250 -0
  11. slidge/contact/__init__.py +8 -0
  12. slidge/contact/contact.py +452 -0
  13. slidge/contact/roster.py +192 -0
  14. slidge/core/__init__.py +2 -0
  15. slidge/core/cache.py +121 -39
  16. slidge/core/config.py +116 -11
  17. slidge/core/gateway/__init__.py +3 -0
  18. slidge/core/gateway/base.py +895 -0
  19. slidge/core/gateway/caps.py +63 -0
  20. slidge/core/gateway/delivery_receipt.py +52 -0
  21. slidge/core/gateway/disco.py +80 -0
  22. slidge/core/gateway/mam.py +75 -0
  23. slidge/core/gateway/muc_admin.py +35 -0
  24. slidge/core/gateway/ping.py +66 -0
  25. slidge/core/gateway/presence.py +95 -0
  26. slidge/core/gateway/registration.py +53 -0
  27. slidge/core/gateway/search.py +102 -0
  28. slidge/core/gateway/session_dispatcher.py +795 -0
  29. slidge/core/gateway/vcard_temp.py +130 -0
  30. slidge/core/mixins/__init__.py +9 -1
  31. slidge/core/mixins/attachment.py +506 -0
  32. slidge/core/mixins/avatar.py +167 -0
  33. slidge/core/mixins/base.py +6 -19
  34. slidge/core/mixins/disco.py +66 -15
  35. slidge/core/mixins/lock.py +31 -0
  36. slidge/core/mixins/message.py +254 -252
  37. slidge/core/mixins/message_maker.py +154 -0
  38. slidge/core/mixins/presence.py +128 -31
  39. slidge/core/mixins/recipient.py +43 -0
  40. slidge/core/pubsub.py +275 -116
  41. slidge/core/session.py +586 -518
  42. slidge/group/__init__.py +10 -0
  43. slidge/group/archive.py +125 -0
  44. slidge/group/bookmarks.py +163 -0
  45. slidge/group/participant.py +458 -0
  46. slidge/group/room.py +1103 -0
  47. slidge/migration.py +18 -0
  48. slidge/slixfix/__init__.py +68 -0
  49. slidge/{util/xep_0050 → slixfix/link_preview}/__init__.py +4 -5
  50. slidge/slixfix/link_preview/link_preview.py +17 -0
  51. slidge/slixfix/link_preview/stanza.py +99 -0
  52. slidge/slixfix/roster.py +60 -0
  53. slidge/{util → slixfix}/xep_0077/register.py +1 -2
  54. slidge/slixfix/xep_0077/stanza.py +104 -0
  55. slidge/{util → slixfix}/xep_0100/gateway.py +17 -12
  56. slidge/slixfix/xep_0153/__init__.py +10 -0
  57. slidge/slixfix/xep_0153/stanza.py +25 -0
  58. slidge/slixfix/xep_0153/vcard_avatar.py +23 -0
  59. slidge/slixfix/xep_0264/__init__.py +5 -0
  60. slidge/slixfix/xep_0264/stanza.py +36 -0
  61. slidge/slixfix/xep_0264/thumbnail.py +23 -0
  62. slidge/slixfix/xep_0292/__init__.py +5 -0
  63. slidge/slixfix/xep_0292/vcard4.py +100 -0
  64. slidge/slixfix/xep_0313/__init__.py +12 -0
  65. slidge/slixfix/xep_0313/mam.py +262 -0
  66. slidge/slixfix/xep_0313/stanza.py +359 -0
  67. slidge/slixfix/xep_0317/__init__.py +5 -0
  68. slidge/slixfix/xep_0317/hats.py +17 -0
  69. slidge/slixfix/xep_0317/stanza.py +28 -0
  70. slidge/{util → slixfix}/xep_0356_old/privilege.py +9 -7
  71. slidge/slixfix/xep_0424/__init__.py +9 -0
  72. slidge/slixfix/xep_0424/retraction.py +77 -0
  73. slidge/slixfix/xep_0424/stanza.py +28 -0
  74. slidge/slixfix/xep_0490/__init__.py +8 -0
  75. slidge/slixfix/xep_0490/mds.py +47 -0
  76. slidge/slixfix/xep_0490/stanza.py +17 -0
  77. slidge/util/__init__.py +4 -6
  78. slidge/util/archive_msg.py +61 -0
  79. slidge/util/conf.py +25 -4
  80. slidge/util/db.py +23 -69
  81. slidge/util/schema.sql +126 -0
  82. slidge/util/sql.py +508 -0
  83. slidge/util/test.py +136 -86
  84. slidge/util/types.py +155 -14
  85. slidge/util/util.py +225 -51
  86. slidge-0.1.2.dist-info/METADATA +111 -0
  87. slidge-0.1.2.dist-info/RECORD +96 -0
  88. {slidge-0.1.0rc1.dist-info → slidge-0.1.2.dist-info}/WHEEL +1 -1
  89. slidge/core/adhoc.py +0 -492
  90. slidge/core/chat_command.py +0 -197
  91. slidge/core/contact.py +0 -441
  92. slidge/core/disco.py +0 -59
  93. slidge/core/gateway.py +0 -899
  94. slidge/core/muc/__init__.py +0 -3
  95. slidge/core/muc/bookmarks.py +0 -74
  96. slidge/core/muc/participant.py +0 -152
  97. slidge/core/muc/room.py +0 -348
  98. slidge/plugins/discord/__init__.py +0 -121
  99. slidge/plugins/discord/client.py +0 -121
  100. slidge/plugins/discord/session.py +0 -172
  101. slidge/plugins/dummy.py +0 -334
  102. slidge/plugins/facebook.py +0 -591
  103. slidge/plugins/hackernews.py +0 -209
  104. slidge/plugins/mattermost/__init__.py +0 -1
  105. slidge/plugins/mattermost/api.py +0 -288
  106. slidge/plugins/mattermost/gateway.py +0 -417
  107. slidge/plugins/mattermost/websocket.py +0 -248
  108. slidge/plugins/signal/__init__.py +0 -4
  109. slidge/plugins/signal/config.py +0 -4
  110. slidge/plugins/signal/contact.py +0 -104
  111. slidge/plugins/signal/gateway.py +0 -379
  112. slidge/plugins/signal/group.py +0 -76
  113. slidge/plugins/signal/session.py +0 -515
  114. slidge/plugins/signal/txt.py +0 -13
  115. slidge/plugins/signal/util.py +0 -32
  116. slidge/plugins/skype.py +0 -310
  117. slidge/plugins/steam.py +0 -400
  118. slidge/plugins/telegram/__init__.py +0 -6
  119. slidge/plugins/telegram/client.py +0 -325
  120. slidge/plugins/telegram/config.py +0 -21
  121. slidge/plugins/telegram/contact.py +0 -154
  122. slidge/plugins/telegram/gateway.py +0 -182
  123. slidge/plugins/telegram/group.py +0 -184
  124. slidge/plugins/telegram/session.py +0 -275
  125. slidge/plugins/telegram/util.py +0 -153
  126. slidge/plugins/whatsapp/__init__.py +0 -6
  127. slidge/plugins/whatsapp/config.py +0 -17
  128. slidge/plugins/whatsapp/contact.py +0 -33
  129. slidge/plugins/whatsapp/event.go +0 -455
  130. slidge/plugins/whatsapp/gateway.go +0 -156
  131. slidge/plugins/whatsapp/gateway.py +0 -69
  132. slidge/plugins/whatsapp/go.mod +0 -17
  133. slidge/plugins/whatsapp/go.sum +0 -22
  134. slidge/plugins/whatsapp/session.go +0 -371
  135. slidge/plugins/whatsapp/session.py +0 -370
  136. slidge/util/xep_0030/__init__.py +0 -13
  137. slidge/util/xep_0030/disco.py +0 -811
  138. slidge/util/xep_0030/stanza/__init__.py +0 -7
  139. slidge/util/xep_0030/stanza/info.py +0 -270
  140. slidge/util/xep_0030/stanza/items.py +0 -147
  141. slidge/util/xep_0030/static.py +0 -467
  142. slidge/util/xep_0050/adhoc.py +0 -631
  143. slidge/util/xep_0050/stanza.py +0 -180
  144. slidge/util/xep_0077/stanza.py +0 -71
  145. slidge/util/xep_0292/__init__.py +0 -1
  146. slidge/util/xep_0292/stanza.py +0 -167
  147. slidge/util/xep_0292/vcard4.py +0 -74
  148. slidge/util/xep_0356/__init__.py +0 -7
  149. slidge/util/xep_0356/permissions.py +0 -35
  150. slidge/util/xep_0356/privilege.py +0 -160
  151. slidge/util/xep_0356/stanza.py +0 -44
  152. slidge/util/xep_0461/__init__.py +0 -6
  153. slidge/util/xep_0461/reply.py +0 -48
  154. slidge/util/xep_0461/stanza.py +0 -80
  155. slidge-0.1.0rc1.dist-info/METADATA +0 -171
  156. slidge-0.1.0rc1.dist-info/RECORD +0 -99
  157. /slidge/{plugins/__init__.py → py.typed} +0 -0
  158. /slidge/{util → slixfix}/xep_0077/__init__.py +0 -0
  159. /slidge/{util → slixfix}/xep_0100/__init__.py +0 -0
  160. /slidge/{util → slixfix}/xep_0100/stanza.py +0 -0
  161. /slidge/{util → slixfix}/xep_0356_old/__init__.py +0 -0
  162. /slidge/{util → slixfix}/xep_0356_old/stanza.py +0 -0
  163. {slidge-0.1.0rc1.dist-info → slidge-0.1.2.dist-info}/LICENSE +0 -0
  164. {slidge-0.1.0rc1.dist-info → slidge-0.1.2.dist-info}/entry_points.txt +0 -0
slidge/util/test.py CHANGED
@@ -2,11 +2,13 @@
2
2
  import tempfile
3
3
  import types
4
4
  from pathlib import Path
5
- from typing import Union
5
+ from typing import Optional, Union
6
6
  from xml.dom.minidom import parseString
7
7
 
8
+ import xmldiff.main
8
9
  from slixmpp import (
9
10
  ElementBase,
11
+ Iq,
10
12
  MatcherId,
11
13
  MatchXMLMask,
12
14
  MatchXPath,
@@ -14,100 +16,31 @@ from slixmpp import (
14
16
  Presence,
15
17
  StanzaPath,
16
18
  )
19
+ from slixmpp.stanza.error import Error
17
20
  from slixmpp.test import SlixTest, TestTransport
18
21
  from slixmpp.xmlstream import highlight, tostring
19
22
  from slixmpp.xmlstream.matcher import MatchIDSender
20
23
 
21
- from slidge import *
24
+ from slidge import (
25
+ BaseGateway,
26
+ BaseSession,
27
+ LegacyBookmarks,
28
+ LegacyContact,
29
+ LegacyMUC,
30
+ LegacyParticipant,
31
+ LegacyRoster,
32
+ user_store,
33
+ )
22
34
 
35
+ from ..command import Command
23
36
  from ..core import config
37
+ from ..core.config import _TimedeltaSeconds
24
38
 
25
39
 
26
- class SlidgeTest(SlixTest):
27
- plugin: Union[types.ModuleType, dict]
28
-
29
- class Config:
30
- jid = "aim.shakespeare.lit"
31
- secret = "test"
32
- server = "shakespeare.lit"
33
- port = 5222
34
- upload_service = "upload.test"
35
- home_dir = Path(tempfile.mkdtemp())
36
- user_jid_validator = ".*@shakespeare.lit"
37
- admins: list[str] = []
38
- no_roster_push = False
39
- upload_requester = None
40
- ignore_delay_threshold = 300
41
-
42
- @classmethod
43
- def setUpClass(cls):
44
- user_store.set_file(Path(tempfile.mkdtemp()) / "test.db")
45
- for k, v in vars(cls.Config).items():
46
- setattr(config, k.upper(), v)
47
-
40
+ class SlixTestPlus(SlixTest):
48
41
  def setUp(self):
49
- BaseGateway._subclass = find_subclass(self.plugin, BaseGateway)
50
- BaseSession._subclass = find_subclass(self.plugin, BaseSession)
51
- LegacyRoster._subclass = find_subclass(self.plugin, LegacyRoster, base_ok=True)
52
- LegacyContact._subclass = find_subclass(
53
- self.plugin, LegacyContact, base_ok=True
54
- )
55
- LegacyMUC._subclass = find_subclass(self.plugin, LegacyMUC, base_ok=True)
56
- LegacyBookmarks._subclass = find_subclass(
57
- self.plugin, LegacyBookmarks, base_ok=True
58
- )
59
-
60
- self.xmpp = BaseGateway.get_self_or_unique_subclass()()
61
-
62
- self.xmpp._always_send_everything = True
63
-
64
- self.xmpp.connection_made(TestTransport(self.xmpp))
65
- self.xmpp.session_bind_event.set()
66
- # Remove unique ID prefix to make it easier to test
67
- self.xmpp._id_prefix = ""
68
- self.xmpp.default_lang = None
69
- self.xmpp.peer_default_lang = None
70
-
71
- def new_id():
72
- self.xmpp._id += 1
73
- return str(self.xmpp._id)
74
-
75
- self.xmpp._id = 0
76
- self.xmpp.new_id = new_id
77
-
78
- # Must have the stream header ready for xmpp.process() to work.
79
- header = self.xmpp.stream_header
80
-
81
- self.xmpp.data_received(header)
82
- self.wait_for_send_queue()
83
-
84
- self.xmpp.socket.next_sent()
85
- self.xmpp.socket.next_sent()
86
-
87
- # Some plugins require messages to have ID values. Set
88
- # this to True in tests related to those plugins.
89
- self.xmpp.use_message_ids = False
90
- self.xmpp.use_presence_ids = False
91
-
92
- @classmethod
93
- def tearDownClass(cls):
94
- BaseSession.reset_subclass()
95
- BaseGateway.reset_subclass()
96
- LegacyRoster.reset_subclass()
97
- LegacyContact.reset_subclass()
98
- LegacyMUC.reset_subclass()
99
- LegacyBookmarks.reset_subclass()
100
- user_store._users = None
101
-
102
- def next_sent(self):
103
- self.wait_for_send_queue()
104
- sent = self.xmpp.socket.next_sent(timeout=1)
105
- if sent is None:
106
- return None
107
- xml = self.parse_xml(sent)
108
- self.fix_namespaces(xml, "jabber:component:accept")
109
- sent = self.xmpp._build_stanza(xml, "jabber:component:accept")
110
- return sent
42
+ super().setUp()
43
+ Error.namespace = "jabber:component:accept"
111
44
 
112
45
  def check(self, stanza, criteria, method="exact", defaults=None, use_values=True):
113
46
  """
@@ -218,8 +151,102 @@ class SlidgeTest(SlixTest):
218
151
  result = self.compare(xml, stanza.xml, stanza2.xml)
219
152
  stanza_class.namespace = old_ns
220
153
 
154
+ if not result:
155
+ debug += str(
156
+ xmldiff.main.diff_texts(tostring(xml), tostring(stanza.xml))
157
+ )
158
+ if use_values:
159
+ debug += str(
160
+ xmldiff.main.diff_texts(tostring(xml), tostring(stanza2.xml))
161
+ )
221
162
  self.assertTrue(result, debug)
222
163
 
164
+ def next_sent(self, timeout=0.05) -> Optional[Union[Message, Iq, Presence]]:
165
+ self.wait_for_send_queue()
166
+ sent = self.xmpp.socket.next_sent(timeout=timeout)
167
+ if sent is None:
168
+ return None
169
+ xml = self.parse_xml(sent)
170
+ self.fix_namespaces(xml, "jabber:component:accept")
171
+ sent = self.xmpp._build_stanza(xml, "jabber:component:accept")
172
+ return sent
173
+
174
+
175
+ class SlidgeTest(SlixTestPlus):
176
+ plugin: Union[types.ModuleType, dict]
177
+
178
+ class Config:
179
+ jid = "aim.shakespeare.lit"
180
+ secret = "test"
181
+ server = "shakespeare.lit"
182
+ port = 5222
183
+ upload_service = "upload.test"
184
+ home_dir = Path(tempfile.mkdtemp())
185
+ user_jid_validator = ".*"
186
+ admins: list[str] = []
187
+ no_roster_push = False
188
+ upload_requester = None
189
+ ignore_delay_threshold = _TimedeltaSeconds("300")
190
+
191
+ @classmethod
192
+ def setUpClass(cls):
193
+ user_store.set_file(Path(tempfile.mkdtemp()) / "test.db")
194
+ for k, v in vars(cls.Config).items():
195
+ setattr(config, k.upper(), v)
196
+
197
+ def setUp(self):
198
+ if hasattr(self, "plugin"):
199
+ BaseGateway._subclass = find_subclass(self.plugin, BaseGateway)
200
+ BaseSession._subclass = find_subclass(self.plugin, BaseSession)
201
+ LegacyRoster._subclass = find_subclass(
202
+ self.plugin, LegacyRoster, base_ok=True
203
+ )
204
+ LegacyContact._subclass = find_subclass(
205
+ self.plugin, LegacyContact, base_ok=True
206
+ )
207
+ LegacyMUC._subclass = find_subclass(self.plugin, LegacyMUC, base_ok=True)
208
+ LegacyBookmarks._subclass = find_subclass(
209
+ self.plugin, LegacyBookmarks, base_ok=True
210
+ )
211
+
212
+ self.xmpp = BaseGateway.get_self_or_unique_subclass()()
213
+
214
+ self.xmpp._always_send_everything = True
215
+
216
+ self.xmpp.connection_made(TestTransport(self.xmpp))
217
+ self.xmpp.session_bind_event.set()
218
+ # Remove unique ID prefix to make it easier to test
219
+ self.xmpp._id_prefix = ""
220
+ self.xmpp.default_lang = None
221
+ self.xmpp.peer_default_lang = None
222
+
223
+ def new_id():
224
+ self.xmpp._id += 1
225
+ return str(self.xmpp._id)
226
+
227
+ self.xmpp._id = 0
228
+ self.xmpp.new_id = new_id
229
+
230
+ # Must have the stream header ready for xmpp.process() to work.
231
+ header = self.xmpp.stream_header
232
+
233
+ self.xmpp.data_received(header)
234
+ self.wait_for_send_queue()
235
+
236
+ self.xmpp.socket.next_sent()
237
+ self.xmpp.socket.next_sent()
238
+
239
+ # Some plugins require messages to have ID values. Set
240
+ # this to True in tests related to those plugins.
241
+ self.xmpp.use_message_ids = False
242
+ self.xmpp.use_presence_ids = False
243
+ Error.namespace = "jabber:component:accept"
244
+
245
+ @classmethod
246
+ def tearDownClass(cls):
247
+ reset_subclasses()
248
+ user_store._users = None
249
+
223
250
 
224
251
  def format_stanza(stanza):
225
252
  return highlight(
@@ -243,3 +270,26 @@ def find_subclass(o, parent, base_ok=False):
243
270
  return parent
244
271
  else:
245
272
  raise RuntimeError
273
+
274
+
275
+ def reset_subclasses():
276
+ """
277
+ Reset registered subclasses between test classes.
278
+
279
+ Needed because these classes are meant to only be subclassed once and raise
280
+ exceptions otherwise.
281
+ """
282
+ BaseSession.reset_subclass()
283
+ BaseGateway.reset_subclass()
284
+ LegacyRoster.reset_subclass()
285
+ LegacyContact.reset_subclass()
286
+ LegacyMUC.reset_subclass()
287
+ LegacyBookmarks.reset_subclass()
288
+ LegacyParticipant.reset_subclass()
289
+ # reset_commands()
290
+
291
+
292
+ def reset_commands():
293
+ Command.subclasses = [
294
+ c for c in Command.subclasses if str(c).startswith("<class 'slidge.core")
295
+ ]
slidge/util/types.py CHANGED
@@ -1,39 +1,180 @@
1
+ """
2
+ Typing stuff
3
+ """
4
+
5
+ from dataclasses import dataclass
6
+ from enum import IntEnum
1
7
  from pathlib import Path
2
- from typing import TYPE_CHECKING, Hashable, Literal, TypeVar, Union
8
+ from typing import (
9
+ IO,
10
+ TYPE_CHECKING,
11
+ Any,
12
+ Generic,
13
+ Hashable,
14
+ Literal,
15
+ NamedTuple,
16
+ Optional,
17
+ TypedDict,
18
+ TypeVar,
19
+ Union,
20
+ )
21
+
22
+ from slixmpp import Message, Presence
23
+ from slixmpp.types import PresenceShows
3
24
 
4
25
  if TYPE_CHECKING:
5
- from ..core.contact import LegacyContact, LegacyRoster
6
- from ..core.gateway import BaseGateway
7
- from ..core.muc.bookmarks import LegacyBookmarks
8
- from ..core.muc.participant import LegacyParticipant
9
- from ..core.muc.room import LegacyMUC
26
+ from ..contact import LegacyContact
10
27
  from ..core.pubsub import PepItem
11
28
  from ..core.session import BaseSession
29
+ from ..group.participant import LegacyMUC, LegacyParticipant
30
+ from .db import GatewayUser
31
+
32
+ AnyBaseSession = BaseSession[Any, Any]
33
+ else:
34
+ AnyBaseSession = None
35
+
36
+
37
+ class URL(str):
38
+ pass
39
+
12
40
 
13
- BookmarksType = TypeVar("BookmarksType", bound="LegacyBookmarks")
14
- GatewayType = TypeVar("GatewayType", bound="BaseGateway")
15
- LegacyContactType = TypeVar("LegacyContactType", bound="LegacyContact")
16
41
  LegacyGroupIdType = TypeVar("LegacyGroupIdType", bound=Hashable)
42
+ """
43
+ Type of the unique identifier for groups, usually a str or an int,
44
+ but anything hashable should work.
45
+ """
17
46
  LegacyMessageType = TypeVar("LegacyMessageType", bound=Hashable)
47
+ LegacyThreadType = TypeVar("LegacyThreadType", bound=Hashable)
48
+ LegacyUserIdType = TypeVar("LegacyUserIdType", bound=Hashable)
49
+
50
+ LegacyContactType = TypeVar("LegacyContactType", bound="LegacyContact")
18
51
  LegacyMUCType = TypeVar("LegacyMUCType", bound="LegacyMUC")
19
52
  LegacyParticipantType = TypeVar("LegacyParticipantType", bound="LegacyParticipant")
20
- LegacyRosterType = TypeVar("LegacyRosterType", bound="LegacyRoster")
21
- LegacyUserIdType = TypeVar("LegacyUserIdType", bound=Hashable)
53
+
22
54
  PepItemType = TypeVar("PepItemType", bound="PepItem")
23
- SessionType = TypeVar("SessionType", bound="BaseSession")
24
- Chat = Union[LegacyMUCType, LegacyContactType]
55
+
56
+ Recipient = Union["LegacyMUC", "LegacyContact"]
57
+ RecipientType = TypeVar("RecipientType", bound=Recipient)
58
+ Sender = Union["LegacyContact", "LegacyParticipant"]
25
59
  AvatarType = Union[bytes, str, Path]
60
+ LegacyFileIdType = Union[int, str]
61
+ AvatarIdType = Union[LegacyFileIdType, URL]
26
62
 
27
63
  ChatState = Literal["active", "composing", "gone", "inactive", "paused"]
28
64
  ProcessingHint = Literal["no-store", "markable", "store"]
29
65
  Marker = Literal["acknowledged", "received", "displayed"]
30
- PresenceShow = Literal["away", "chat", "dnd", "xa"]
31
66
  FieldType = Literal[
32
67
  "boolean",
33
68
  "fixed",
34
69
  "text-single",
35
70
  "jid-single",
71
+ "jid-multi",
36
72
  "list-single",
37
73
  "list-multi",
38
74
  "text-private",
39
75
  ]
76
+ MucAffiliation = Literal["owner", "admin", "member", "outcast", "none"]
77
+ MucRole = Literal["visitor", "participant", "moderator", "none"]
78
+
79
+
80
+ @dataclass
81
+ class MessageReference(Generic[LegacyMessageType]):
82
+ """
83
+ A "message reply", ie a "quoted message" (:xep:`0461`)
84
+
85
+ At the very minimum, the legacy message ID attribute must be set, but to
86
+ ensure that the quote is displayed in all XMPP clients, the author must also
87
+ be set.
88
+ The body is used as a fallback for XMPP clients that do not support :xep:`0461`
89
+ of that failed to find the referenced message.
90
+ """
91
+
92
+ legacy_id: LegacyMessageType
93
+ author: Optional[Union["GatewayUser", "LegacyParticipant", "LegacyContact"]] = None
94
+ body: Optional[str] = None
95
+
96
+
97
+ @dataclass
98
+ class LegacyAttachment:
99
+ """
100
+ A file attachment to a message
101
+
102
+ At the minimum, one of the ``path``, ``steam``, ``data`` or ``url`` attribute
103
+ has to be set
104
+
105
+ To be used with :meth:`.LegacyContact.send_files` or
106
+ :meth:`.LegacyParticipant.send_files`
107
+ """
108
+
109
+ path: Optional[Union[Path, str]] = None
110
+ name: Optional[Union[str]] = None
111
+ stream: Optional[IO[bytes]] = None
112
+ data: Optional[bytes] = None
113
+ content_type: Optional[str] = None
114
+ legacy_file_id: Optional[Union[str, int]] = None
115
+ url: Optional[str] = None
116
+ caption: Optional[str] = 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
+ """
121
+
122
+ def __post_init__(self):
123
+ if not any(
124
+ x is not None for x in (self.path, self.stream, self.data, self.url)
125
+ ):
126
+ raise TypeError("There is not data in this attachment", self)
127
+
128
+
129
+ class MucType(IntEnum):
130
+ """
131
+ The type of group, private, public, anonymous or not.
132
+ """
133
+
134
+ GROUP = 0
135
+ """
136
+ A private group, members-only and non-anonymous, eg a family group.
137
+ """
138
+ CHANNEL = 1
139
+ """
140
+ A public group, aka an anonymous channel.
141
+ """
142
+ CHANNEL_NON_ANONYMOUS = 2
143
+ """
144
+ A public group where participants' legacy IDs are visible to everybody.
145
+ """
146
+
147
+
148
+ PseudoPresenceShow = Union[PresenceShows, Literal[""]]
149
+
150
+
151
+ class ResourceDict(TypedDict):
152
+ show: PseudoPresenceShow
153
+ status: str
154
+ priority: int
155
+
156
+
157
+ MessageOrPresenceTypeVar = TypeVar(
158
+ "MessageOrPresenceTypeVar", bound=Union[Message, Presence]
159
+ )
160
+
161
+
162
+ class LinkPreview(NamedTuple):
163
+ about: str
164
+ title: Optional[str]
165
+ description: Optional[str]
166
+ url: Optional[str]
167
+ image: Optional[str]
168
+ type: Optional[str]
169
+ site_name: Optional[str]
170
+
171
+
172
+ class Mention(NamedTuple):
173
+ contact: "LegacyContact"
174
+ start: int
175
+ end: int
176
+
177
+
178
+ class Hat(NamedTuple):
179
+ uri: str
180
+ title: str