slidge 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- slidge/__init__.py +61 -0
- slidge/__main__.py +192 -0
- slidge/command/__init__.py +28 -0
- slidge/command/adhoc.py +258 -0
- slidge/command/admin.py +193 -0
- slidge/command/base.py +441 -0
- slidge/command/categories.py +3 -0
- slidge/command/chat_command.py +288 -0
- slidge/command/register.py +179 -0
- slidge/command/user.py +250 -0
- slidge/contact/__init__.py +8 -0
- slidge/contact/contact.py +452 -0
- slidge/contact/roster.py +192 -0
- slidge/core/__init__.py +3 -0
- slidge/core/cache.py +183 -0
- slidge/core/config.py +209 -0
- slidge/core/gateway/__init__.py +3 -0
- slidge/core/gateway/base.py +892 -0
- slidge/core/gateway/caps.py +63 -0
- slidge/core/gateway/delivery_receipt.py +52 -0
- slidge/core/gateway/disco.py +80 -0
- slidge/core/gateway/mam.py +75 -0
- slidge/core/gateway/muc_admin.py +35 -0
- slidge/core/gateway/ping.py +66 -0
- slidge/core/gateway/presence.py +95 -0
- slidge/core/gateway/registration.py +53 -0
- slidge/core/gateway/search.py +102 -0
- slidge/core/gateway/session_dispatcher.py +757 -0
- slidge/core/gateway/vcard_temp.py +130 -0
- slidge/core/mixins/__init__.py +19 -0
- slidge/core/mixins/attachment.py +506 -0
- slidge/core/mixins/avatar.py +167 -0
- slidge/core/mixins/base.py +31 -0
- slidge/core/mixins/disco.py +130 -0
- slidge/core/mixins/lock.py +31 -0
- slidge/core/mixins/message.py +398 -0
- slidge/core/mixins/message_maker.py +154 -0
- slidge/core/mixins/presence.py +217 -0
- slidge/core/mixins/recipient.py +43 -0
- slidge/core/pubsub.py +525 -0
- slidge/core/session.py +752 -0
- slidge/group/__init__.py +10 -0
- slidge/group/archive.py +125 -0
- slidge/group/bookmarks.py +163 -0
- slidge/group/participant.py +440 -0
- slidge/group/room.py +1095 -0
- slidge/migration.py +18 -0
- slidge/py.typed +0 -0
- slidge/slixfix/__init__.py +68 -0
- slidge/slixfix/link_preview/__init__.py +10 -0
- slidge/slixfix/link_preview/link_preview.py +17 -0
- slidge/slixfix/link_preview/stanza.py +99 -0
- slidge/slixfix/roster.py +60 -0
- slidge/slixfix/xep_0077/__init__.py +10 -0
- slidge/slixfix/xep_0077/register.py +289 -0
- slidge/slixfix/xep_0077/stanza.py +104 -0
- slidge/slixfix/xep_0100/__init__.py +5 -0
- slidge/slixfix/xep_0100/gateway.py +121 -0
- slidge/slixfix/xep_0100/stanza.py +9 -0
- slidge/slixfix/xep_0153/__init__.py +10 -0
- slidge/slixfix/xep_0153/stanza.py +25 -0
- slidge/slixfix/xep_0153/vcard_avatar.py +23 -0
- slidge/slixfix/xep_0264/__init__.py +5 -0
- slidge/slixfix/xep_0264/stanza.py +36 -0
- slidge/slixfix/xep_0264/thumbnail.py +23 -0
- slidge/slixfix/xep_0292/__init__.py +5 -0
- slidge/slixfix/xep_0292/vcard4.py +100 -0
- slidge/slixfix/xep_0313/__init__.py +12 -0
- slidge/slixfix/xep_0313/mam.py +262 -0
- slidge/slixfix/xep_0313/stanza.py +359 -0
- slidge/slixfix/xep_0317/__init__.py +5 -0
- slidge/slixfix/xep_0317/hats.py +17 -0
- slidge/slixfix/xep_0317/stanza.py +28 -0
- slidge/slixfix/xep_0356_old/__init__.py +7 -0
- slidge/slixfix/xep_0356_old/privilege.py +167 -0
- slidge/slixfix/xep_0356_old/stanza.py +44 -0
- slidge/slixfix/xep_0424/__init__.py +9 -0
- slidge/slixfix/xep_0424/retraction.py +77 -0
- slidge/slixfix/xep_0424/stanza.py +28 -0
- slidge/slixfix/xep_0490/__init__.py +8 -0
- slidge/slixfix/xep_0490/mds.py +47 -0
- slidge/slixfix/xep_0490/stanza.py +17 -0
- slidge/util/__init__.py +15 -0
- slidge/util/archive_msg.py +61 -0
- slidge/util/conf.py +206 -0
- slidge/util/db.py +229 -0
- slidge/util/schema.sql +126 -0
- slidge/util/sql.py +508 -0
- slidge/util/test.py +295 -0
- slidge/util/types.py +180 -0
- slidge/util/util.py +295 -0
- slidge-0.1.0.dist-info/LICENSE +661 -0
- slidge-0.1.0.dist-info/METADATA +109 -0
- slidge-0.1.0.dist-info/RECORD +96 -0
- slidge-0.1.0.dist-info/WHEEL +4 -0
- slidge-0.1.0.dist-info/entry_points.txt +3 -0
slidge/util/test.py
ADDED
@@ -0,0 +1,295 @@
|
|
1
|
+
# type:ignore
|
2
|
+
import tempfile
|
3
|
+
import types
|
4
|
+
from pathlib import Path
|
5
|
+
from typing import Optional, Union
|
6
|
+
from xml.dom.minidom import parseString
|
7
|
+
|
8
|
+
import xmldiff.main
|
9
|
+
from slixmpp import (
|
10
|
+
ElementBase,
|
11
|
+
Iq,
|
12
|
+
MatcherId,
|
13
|
+
MatchXMLMask,
|
14
|
+
MatchXPath,
|
15
|
+
Message,
|
16
|
+
Presence,
|
17
|
+
StanzaPath,
|
18
|
+
)
|
19
|
+
from slixmpp.stanza.error import Error
|
20
|
+
from slixmpp.test import SlixTest, TestTransport
|
21
|
+
from slixmpp.xmlstream import highlight, tostring
|
22
|
+
from slixmpp.xmlstream.matcher import MatchIDSender
|
23
|
+
|
24
|
+
from slidge import (
|
25
|
+
BaseGateway,
|
26
|
+
BaseSession,
|
27
|
+
LegacyBookmarks,
|
28
|
+
LegacyContact,
|
29
|
+
LegacyMUC,
|
30
|
+
LegacyParticipant,
|
31
|
+
LegacyRoster,
|
32
|
+
user_store,
|
33
|
+
)
|
34
|
+
|
35
|
+
from ..command import Command
|
36
|
+
from ..core import config
|
37
|
+
from ..core.config import _TimedeltaSeconds
|
38
|
+
|
39
|
+
|
40
|
+
class SlixTestPlus(SlixTest):
|
41
|
+
def setUp(self):
|
42
|
+
super().setUp()
|
43
|
+
Error.namespace = "jabber:component:accept"
|
44
|
+
|
45
|
+
def check(self, stanza, criteria, method="exact", defaults=None, use_values=True):
|
46
|
+
"""
|
47
|
+
Create and compare several stanza objects to a correct XML string.
|
48
|
+
|
49
|
+
If use_values is False, tests using stanza.values will not be used.
|
50
|
+
|
51
|
+
Some stanzas provide default values for some interfaces, but
|
52
|
+
these defaults can be problematic for testing since they can easily
|
53
|
+
be forgotten when supplying the XML string. A list of interfaces that
|
54
|
+
use defaults may be provided and the generated stanzas will use the
|
55
|
+
default values for those interfaces if needed.
|
56
|
+
|
57
|
+
However, correcting the supplied XML is not possible for interfaces
|
58
|
+
that add or remove XML elements. Only interfaces that map to XML
|
59
|
+
attributes may be set using the defaults parameter. The supplied XML
|
60
|
+
must take into account any extra elements that are included by default.
|
61
|
+
|
62
|
+
Arguments:
|
63
|
+
stanza -- The stanza object to test.
|
64
|
+
criteria -- An expression the stanza must match against.
|
65
|
+
method -- The type of matching to use; one of:
|
66
|
+
'exact', 'mask', 'id', 'xpath', and 'stanzapath'.
|
67
|
+
Defaults to the value of self.match_method.
|
68
|
+
defaults -- A list of stanza interfaces that have default
|
69
|
+
values. These interfaces will be set to their
|
70
|
+
defaults for the given and generated stanzas to
|
71
|
+
prevent unexpected test failures.
|
72
|
+
use_values -- Indicates if testing using stanza.values should
|
73
|
+
be used. Defaults to True.
|
74
|
+
"""
|
75
|
+
if method is None and hasattr(self, "match_method"):
|
76
|
+
method = getattr(self, "match_method")
|
77
|
+
|
78
|
+
if method != "exact":
|
79
|
+
matchers = {
|
80
|
+
"stanzapath": StanzaPath,
|
81
|
+
"xpath": MatchXPath,
|
82
|
+
"mask": MatchXMLMask,
|
83
|
+
"idsender": MatchIDSender,
|
84
|
+
"id": MatcherId,
|
85
|
+
}
|
86
|
+
Matcher = matchers.get(method, None)
|
87
|
+
if Matcher is None:
|
88
|
+
raise ValueError("Unknown matching method.")
|
89
|
+
test = Matcher(criteria)
|
90
|
+
self.assertTrue(
|
91
|
+
test.match(stanza),
|
92
|
+
"Stanza did not match using %s method:\n" % method
|
93
|
+
+ "Criteria:\n%s\n" % str(criteria)
|
94
|
+
+ "Stanza:\n%s" % str(stanza),
|
95
|
+
)
|
96
|
+
else:
|
97
|
+
stanza_class = stanza.__class__
|
98
|
+
# Hack to preserve namespaces instead of having jabber:client
|
99
|
+
# everywhere.
|
100
|
+
old_ns = stanza_class.namespace
|
101
|
+
stanza_class.namespace = stanza.namespace
|
102
|
+
if not isinstance(criteria, ElementBase):
|
103
|
+
xml = self.parse_xml(criteria)
|
104
|
+
else:
|
105
|
+
xml = criteria.xml
|
106
|
+
|
107
|
+
# Ensure that top level namespaces are used, even if they
|
108
|
+
# were not provided.
|
109
|
+
self.fix_namespaces(stanza.xml)
|
110
|
+
self.fix_namespaces(xml)
|
111
|
+
|
112
|
+
stanza2 = stanza_class(xml=xml)
|
113
|
+
|
114
|
+
if use_values:
|
115
|
+
# Using stanza.values will add XML for any interface that
|
116
|
+
# has a default value. We need to set those defaults on
|
117
|
+
# the existing stanzas and XML so that they will compare
|
118
|
+
# correctly.
|
119
|
+
default_stanza = stanza_class()
|
120
|
+
if defaults is None:
|
121
|
+
known_defaults = {Message: ["type"], Presence: ["priority"]}
|
122
|
+
defaults = known_defaults.get(stanza_class, [])
|
123
|
+
for interface in defaults:
|
124
|
+
stanza[interface] = stanza[interface]
|
125
|
+
stanza2[interface] = stanza2[interface]
|
126
|
+
# Can really only automatically add defaults for top
|
127
|
+
# level attribute values. Anything else must be accounted
|
128
|
+
# for in the provided XML string.
|
129
|
+
if interface not in xml.attrib:
|
130
|
+
if interface in default_stanza.xml.attrib:
|
131
|
+
value = default_stanza.xml.attrib[interface]
|
132
|
+
xml.attrib[interface] = value
|
133
|
+
|
134
|
+
values = stanza2.values
|
135
|
+
stanza3 = stanza_class()
|
136
|
+
stanza3.values = values
|
137
|
+
|
138
|
+
debug = "Three methods for creating stanzas do not match.\n"
|
139
|
+
debug += "Given XML:\n%s\n" % highlight(tostring(xml))
|
140
|
+
debug += "Given stanza:\n%s\n" % format_stanza(stanza)
|
141
|
+
debug += "Generated stanza:\n%s\n" % highlight(tostring(stanza2.xml))
|
142
|
+
debug += "Second generated stanza:\n%s\n" % highlight(
|
143
|
+
tostring(stanza3.xml)
|
144
|
+
)
|
145
|
+
result = self.compare(xml, stanza.xml, stanza2.xml, stanza3.xml)
|
146
|
+
else:
|
147
|
+
debug = "Two methods for creating stanzas do not match.\n"
|
148
|
+
debug += "Given XML:\n%s\n" % highlight(tostring(xml))
|
149
|
+
debug += "Given stanza:\n%s\n" % format_stanza(stanza)
|
150
|
+
debug += "Generated stanza:\n%s\n" % highlight(tostring(stanza2.xml))
|
151
|
+
result = self.compare(xml, stanza.xml, stanza2.xml)
|
152
|
+
stanza_class.namespace = old_ns
|
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
|
+
)
|
162
|
+
self.assertTrue(result, debug)
|
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
|
+
|
250
|
+
|
251
|
+
def format_stanza(stanza):
|
252
|
+
return highlight(
|
253
|
+
"\n".join(parseString(tostring(stanza.xml)).toprettyxml().split("\n")[1:])
|
254
|
+
)
|
255
|
+
|
256
|
+
|
257
|
+
def find_subclass(o, parent, base_ok=False):
|
258
|
+
try:
|
259
|
+
vals = vars(o).values()
|
260
|
+
except TypeError:
|
261
|
+
vals = o.values()
|
262
|
+
for x in vals:
|
263
|
+
try:
|
264
|
+
if issubclass(x, parent) and x is not parent:
|
265
|
+
return x
|
266
|
+
except TypeError:
|
267
|
+
pass
|
268
|
+
else:
|
269
|
+
if base_ok:
|
270
|
+
return parent
|
271
|
+
else:
|
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
ADDED
@@ -0,0 +1,180 @@
|
|
1
|
+
"""
|
2
|
+
Typing stuff
|
3
|
+
"""
|
4
|
+
|
5
|
+
from dataclasses import dataclass
|
6
|
+
from enum import IntEnum
|
7
|
+
from pathlib import Path
|
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
|
24
|
+
|
25
|
+
if TYPE_CHECKING:
|
26
|
+
from ..contact import LegacyContact
|
27
|
+
from ..core.pubsub import PepItem
|
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
|
+
|
40
|
+
|
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
|
+
"""
|
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")
|
51
|
+
LegacyMUCType = TypeVar("LegacyMUCType", bound="LegacyMUC")
|
52
|
+
LegacyParticipantType = TypeVar("LegacyParticipantType", bound="LegacyParticipant")
|
53
|
+
|
54
|
+
PepItemType = TypeVar("PepItemType", bound="PepItem")
|
55
|
+
|
56
|
+
Recipient = Union["LegacyMUC", "LegacyContact"]
|
57
|
+
RecipientType = TypeVar("RecipientType", bound=Recipient)
|
58
|
+
Sender = Union["LegacyContact", "LegacyParticipant"]
|
59
|
+
AvatarType = Union[bytes, str, Path]
|
60
|
+
LegacyFileIdType = Union[int, str]
|
61
|
+
AvatarIdType = Union[LegacyFileIdType, URL]
|
62
|
+
|
63
|
+
ChatState = Literal["active", "composing", "gone", "inactive", "paused"]
|
64
|
+
ProcessingHint = Literal["no-store", "markable", "store"]
|
65
|
+
Marker = Literal["acknowledged", "received", "displayed"]
|
66
|
+
FieldType = Literal[
|
67
|
+
"boolean",
|
68
|
+
"fixed",
|
69
|
+
"text-single",
|
70
|
+
"jid-single",
|
71
|
+
"jid-multi",
|
72
|
+
"list-single",
|
73
|
+
"list-multi",
|
74
|
+
"text-private",
|
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
|