slidge 0.2.12__py3-none-any.whl → 0.3.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 +5 -2
- slidge/command/adhoc.py +9 -3
- slidge/command/admin.py +16 -12
- slidge/command/base.py +16 -12
- slidge/command/chat_command.py +25 -16
- slidge/command/user.py +7 -8
- slidge/contact/contact.py +123 -210
- slidge/contact/roster.py +108 -105
- slidge/core/config.py +2 -43
- slidge/core/dispatcher/caps.py +9 -2
- slidge/core/dispatcher/disco.py +13 -3
- slidge/core/dispatcher/message/__init__.py +1 -1
- slidge/core/dispatcher/message/chat_state.py +17 -8
- slidge/core/dispatcher/message/marker.py +7 -5
- slidge/core/dispatcher/message/message.py +120 -93
- slidge/core/dispatcher/muc/__init__.py +1 -1
- slidge/core/dispatcher/muc/admin.py +4 -4
- slidge/core/dispatcher/muc/mam.py +10 -6
- slidge/core/dispatcher/muc/misc.py +4 -2
- slidge/core/dispatcher/muc/owner.py +5 -3
- slidge/core/dispatcher/muc/ping.py +3 -1
- slidge/core/dispatcher/presence.py +26 -15
- slidge/core/dispatcher/registration.py +20 -12
- slidge/core/dispatcher/search.py +7 -3
- slidge/core/dispatcher/session_dispatcher.py +13 -5
- slidge/core/dispatcher/util.py +37 -27
- slidge/core/dispatcher/vcard.py +7 -4
- slidge/core/gateway.py +177 -87
- slidge/core/mixins/__init__.py +1 -11
- slidge/core/mixins/attachment.py +200 -147
- slidge/core/mixins/avatar.py +105 -177
- slidge/core/mixins/base.py +3 -1
- slidge/core/mixins/db.py +50 -2
- slidge/core/mixins/disco.py +1 -1
- slidge/core/mixins/message.py +19 -17
- slidge/core/mixins/message_maker.py +29 -15
- slidge/core/mixins/message_text.py +67 -30
- slidge/core/mixins/presence.py +94 -37
- slidge/core/pubsub.py +42 -47
- slidge/core/session.py +95 -60
- slidge/db/alembic/versions/cef02a8b1451_initial_schema.py +361 -0
- slidge/db/avatar.py +150 -119
- slidge/db/meta.py +33 -22
- slidge/db/models.py +69 -117
- slidge/db/store.py +414 -1094
- slidge/group/archive.py +65 -55
- slidge/group/bookmarks.py +96 -59
- slidge/group/participant.py +150 -144
- slidge/group/room.py +345 -327
- slidge/main.py +34 -22
- slidge/migration.py +17 -29
- slidge/slixfix/__init__.py +20 -4
- slidge/slixfix/delivery_receipt.py +6 -4
- slidge/slixfix/link_preview/link_preview.py +1 -1
- slidge/slixfix/link_preview/stanza.py +1 -1
- slidge/slixfix/roster.py +5 -7
- slidge/slixfix/xep_0077/register.py +8 -8
- slidge/slixfix/xep_0077/stanza.py +7 -7
- slidge/slixfix/xep_0100/gateway.py +12 -13
- slidge/slixfix/xep_0153/vcard_avatar.py +1 -1
- slidge/slixfix/xep_0292/vcard4.py +12 -2
- slidge/util/archive_msg.py +11 -5
- slidge/util/conf.py +27 -21
- slidge/util/jid_escaping.py +1 -1
- slidge/{core/mixins → util}/lock.py +6 -6
- slidge/util/test.py +30 -29
- slidge/util/types.py +24 -18
- slidge/util/util.py +26 -22
- {slidge-0.2.12.dist-info → slidge-0.3.0.dist-info}/METADATA +1 -1
- slidge-0.3.0.dist-info/RECORD +95 -0
- {slidge-0.2.12.dist-info → slidge-0.3.0.dist-info}/WHEEL +1 -1
- slidge/db/alembic/versions/04cf35e3cf85_add_participant_nickname_no_illegal.py +0 -33
- slidge/db/alembic/versions/09f27f098baa_add_missing_attributes_in_room.py +0 -36
- slidge/db/alembic/versions/15b0bd83407a_remove_bogus_unique_constraints_on_room_.py +0 -85
- slidge/db/alembic/versions/2461390c0af2_store_contacts_caps_verstring_in_db.py +0 -36
- slidge/db/alembic/versions/29f5280c61aa_store_subject_setter_in_room.py +0 -37
- slidge/db/alembic/versions/2b1f45ab7379_store_room_subject_setter_by_nickname.py +0 -41
- slidge/db/alembic/versions/3071e0fa69d4_add_contact_client_type.py +0 -52
- slidge/db/alembic/versions/45c24cc73c91_add_bob.py +0 -42
- slidge/db/alembic/versions/5bd48bfdffa2_lift_room_legacy_id_constraint.py +0 -61
- slidge/db/alembic/versions/82a4af84b679_add_muc_history_filled.py +0 -48
- slidge/db/alembic/versions/8b993243a536_add_vcard_content_to_contact_table.py +0 -43
- slidge/db/alembic/versions/8d2ced764698_rely_on_db_to_store_contacts_rooms_and_.py +0 -139
- slidge/db/alembic/versions/aa9d82a7f6ef_db_creation.py +0 -50
- slidge/db/alembic/versions/abba1ae0edb3_store_avatar_legacy_id_in_the_contact_.py +0 -79
- slidge/db/alembic/versions/b33993e87db3_move_everything_to_persistent_db.py +0 -214
- slidge/db/alembic/versions/b64b1a793483_add_source_and_legacy_id_for_archived_.py +0 -52
- slidge/db/alembic/versions/c4a8ec35a0e8_per_room_user_nick.py +0 -34
- slidge/db/alembic/versions/e91195719c2c_store_users_avatars_persistently.py +0 -26
- slidge-0.2.12.dist-info/RECORD +0 -112
- {slidge-0.2.12.dist-info → slidge-0.3.0.dist-info}/entry_points.txt +0 -0
- {slidge-0.2.12.dist-info → slidge-0.3.0.dist-info}/licenses/LICENSE +0 -0
- {slidge-0.2.12.dist-info → slidge-0.3.0.dist-info}/top_level.txt +0 -0
slidge/core/mixins/attachment.py
CHANGED
@@ -11,7 +11,7 @@ from datetime import datetime
|
|
11
11
|
from itertools import chain
|
12
12
|
from mimetypes import guess_extension, guess_type
|
13
13
|
from pathlib import Path
|
14
|
-
from typing import
|
14
|
+
from typing import Collection, Optional, Sequence, Union
|
15
15
|
from urllib.parse import quote as urlquote
|
16
16
|
from uuid import uuid4
|
17
17
|
from xml.etree import ElementTree as ET
|
@@ -25,6 +25,7 @@ from slixmpp.plugins.xep_0363 import FileUploadError
|
|
25
25
|
from slixmpp.plugins.xep_0447.stanza import StatelessFileSharing
|
26
26
|
|
27
27
|
from ...db.avatar import avatar_cache
|
28
|
+
from ...db.models import Attachment
|
28
29
|
from ...util.types import (
|
29
30
|
LegacyAttachment,
|
30
31
|
LegacyMessageType,
|
@@ -37,9 +38,7 @@ from .message_text import TextMessageMixin
|
|
37
38
|
|
38
39
|
|
39
40
|
class AttachmentMixin(TextMessageMixin):
|
40
|
-
|
41
|
-
super().__init__(*a, **kw)
|
42
|
-
self.__store = self.xmpp.store.attachments
|
41
|
+
is_group: bool
|
43
42
|
|
44
43
|
async def __upload(
|
45
44
|
self,
|
@@ -137,28 +136,44 @@ class AttachmentMixin(TextMessageMixin):
|
|
137
136
|
|
138
137
|
return destination, uploaded_url
|
139
138
|
|
139
|
+
async def __valid_url(self, url: str) -> bool:
|
140
|
+
async with self.session.http.head(url) as r:
|
141
|
+
return r.status < 400
|
142
|
+
|
143
|
+
async def __get_stored(self, attachment: LegacyAttachment) -> Attachment:
|
144
|
+
if attachment.legacy_file_id is not None and self.session is not NotImplemented:
|
145
|
+
with self.xmpp.store.session() as orm:
|
146
|
+
stored = (
|
147
|
+
orm.query(Attachment)
|
148
|
+
.filter_by(
|
149
|
+
legacy_file_id=str(attachment.legacy_file_id),
|
150
|
+
user_account_id=self.session.user_pk,
|
151
|
+
)
|
152
|
+
.one_or_none()
|
153
|
+
)
|
154
|
+
if stored is not None:
|
155
|
+
if not await self.__valid_url(stored.url):
|
156
|
+
stored.url = None # type:ignore
|
157
|
+
return stored
|
158
|
+
return Attachment(
|
159
|
+
user_account_id=None
|
160
|
+
if self.session is NotImplemented
|
161
|
+
else self.session.user_pk,
|
162
|
+
legacy_file_id=None
|
163
|
+
if attachment.legacy_file_id is None
|
164
|
+
else str(attachment.legacy_file_id),
|
165
|
+
url=attachment.url,
|
166
|
+
)
|
167
|
+
|
140
168
|
async def __get_url(
|
141
|
-
self,
|
142
|
-
file_path: Optional[Path] = None,
|
143
|
-
async_data_stream: Optional[AsyncIterator[bytes]] = None,
|
144
|
-
data_stream: Optional[IO[bytes]] = None,
|
145
|
-
data: Optional[bytes] = None,
|
146
|
-
file_url: Optional[str] = None,
|
147
|
-
file_name: Optional[str] = None,
|
148
|
-
content_type: Optional[str] = None,
|
149
|
-
legacy_file_id: Optional[Union[str, int]] = None,
|
169
|
+
self, attachment: LegacyAttachment, stored: Attachment
|
150
170
|
) -> tuple[bool, Optional[Path], str]:
|
151
|
-
if
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
else:
|
158
|
-
self.__store.remove(str(legacy_file_id))
|
159
|
-
|
160
|
-
if file_url and config.USE_ATTACHMENT_ORIGINAL_URLS:
|
161
|
-
return False, None, file_url
|
171
|
+
if attachment.url and config.USE_ATTACHMENT_ORIGINAL_URLS:
|
172
|
+
return False, None, attachment.url
|
173
|
+
|
174
|
+
file_name = attachment.name
|
175
|
+
content_type = attachment.content_type
|
176
|
+
file_path = attachment.path
|
162
177
|
|
163
178
|
if file_name and len(file_name) > config.ATTACHMENT_MAXIMUM_FILE_NAME_LENGTH:
|
164
179
|
log.debug("Trimming long filename: %s", file_name)
|
@@ -176,47 +191,48 @@ class AttachmentMixin(TextMessageMixin):
|
|
176
191
|
file_name += ext
|
177
192
|
temp_dir = Path(tempfile.mkdtemp())
|
178
193
|
file_path = temp_dir / file_name
|
179
|
-
if
|
180
|
-
async with self.session.http.get(
|
194
|
+
if attachment.url:
|
195
|
+
async with self.session.http.get(attachment.url) as r:
|
181
196
|
r.raise_for_status()
|
182
197
|
with file_path.open("wb") as f:
|
183
198
|
f.write(await r.read())
|
184
199
|
|
185
|
-
elif
|
186
|
-
data =
|
200
|
+
elif attachment.stream is not None:
|
201
|
+
data = attachment.stream.read()
|
187
202
|
if data is None:
|
188
203
|
raise RuntimeError
|
189
204
|
|
190
205
|
with file_path.open("wb") as f:
|
191
206
|
f.write(data)
|
192
|
-
elif
|
207
|
+
elif attachment.aio_stream is not None:
|
193
208
|
# TODO: patch slixmpp to allow this as data source for
|
194
209
|
# upload_file() so we don't even have to write anything
|
195
210
|
# to disk.
|
196
211
|
with file_path.open("wb") as f:
|
197
|
-
async for chunk in
|
212
|
+
async for chunk in attachment.aio_stream:
|
198
213
|
f.write(chunk)
|
199
|
-
elif data is not None:
|
214
|
+
elif attachment.data is not None:
|
200
215
|
with file_path.open("wb") as f:
|
201
|
-
f.write(data)
|
216
|
+
f.write(attachment.data)
|
202
217
|
|
203
218
|
is_temp = not bool(config.NO_UPLOAD_PATH)
|
204
219
|
else:
|
205
220
|
is_temp = False
|
206
221
|
|
222
|
+
assert isinstance(file_path, Path)
|
207
223
|
if config.FIX_FILENAME_SUFFIX_MIME_TYPE:
|
208
224
|
file_name = str(fix_suffix(file_path, content_type, file_name))
|
209
225
|
|
210
226
|
if config.NO_UPLOAD_PATH:
|
211
227
|
local_path, new_url = await self.__no_upload(
|
212
|
-
file_path, file_name, legacy_file_id
|
228
|
+
file_path, file_name, stored.legacy_file_id
|
213
229
|
)
|
214
230
|
new_url = (config.NO_UPLOAD_URL_PREFIX or "") + "/" + urlquote(new_url)
|
215
231
|
else:
|
216
232
|
local_path = file_path
|
217
233
|
new_url = await self.__upload(file_path, file_name, content_type)
|
218
|
-
if legacy_file_id and new_url is not None:
|
219
|
-
|
234
|
+
if stored.legacy_file_id and new_url is not None:
|
235
|
+
stored.url = new_url
|
220
236
|
|
221
237
|
return is_temp, local_path, new_url
|
222
238
|
|
@@ -225,13 +241,11 @@ class AttachmentMixin(TextMessageMixin):
|
|
225
241
|
msg: Message,
|
226
242
|
uploaded_url: str,
|
227
243
|
path: Optional[Path],
|
228
|
-
|
229
|
-
|
230
|
-
file_name: Optional[str] = None,
|
244
|
+
attachment: LegacyAttachment,
|
245
|
+
stored: Attachment,
|
231
246
|
) -> Thumbnail | None:
|
232
|
-
|
233
|
-
|
234
|
-
ref = self.xmpp["xep_0372"].stanza.Reference(xml=ET.fromstring(cache))
|
247
|
+
if stored.sims is not None:
|
248
|
+
ref = self.xmpp["xep_0372"].stanza.Reference(xml=ET.fromstring(stored.sims))
|
235
249
|
msg.append(ref)
|
236
250
|
if ref["sims"]["file"].get_plugin("thumbnail", check=True):
|
237
251
|
return ref["sims"]["file"]["thumbnail"]
|
@@ -242,12 +256,14 @@ class AttachmentMixin(TextMessageMixin):
|
|
242
256
|
return None
|
243
257
|
|
244
258
|
ref = self.xmpp["xep_0385"].get_sims(
|
245
|
-
path, [uploaded_url], content_type, caption
|
259
|
+
path, [uploaded_url], attachment.content_type, attachment.caption
|
246
260
|
)
|
247
|
-
if
|
248
|
-
ref["sims"]["file"]["name"] =
|
261
|
+
if attachment.name:
|
262
|
+
ref["sims"]["file"]["name"] = attachment.name
|
249
263
|
thumbnail = None
|
250
|
-
if content_type is not None and content_type.startswith(
|
264
|
+
if attachment.content_type is not None and attachment.content_type.startswith(
|
265
|
+
"image"
|
266
|
+
):
|
251
267
|
try:
|
252
268
|
h, x, y = await self.xmpp.loop.run_in_executor(
|
253
269
|
avatar_cache._thread_pool, get_thumbhash, path
|
@@ -261,8 +277,7 @@ class AttachmentMixin(TextMessageMixin):
|
|
261
277
|
thumbnail["media-type"] = "image/thumbhash"
|
262
278
|
thumbnail["uri"] = "data:image/thumbhash;base64," + urlquote(h)
|
263
279
|
|
264
|
-
|
265
|
-
|
280
|
+
stored.sims = str(ref)
|
266
281
|
msg.append(ref)
|
267
282
|
|
268
283
|
return thumbnail
|
@@ -272,26 +287,29 @@ class AttachmentMixin(TextMessageMixin):
|
|
272
287
|
msg: Message,
|
273
288
|
uploaded_url: str,
|
274
289
|
path: Optional[Path],
|
275
|
-
|
276
|
-
|
277
|
-
file_name: Optional[str] = None,
|
290
|
+
attachment: LegacyAttachment,
|
291
|
+
stored: Attachment,
|
278
292
|
thumbnail: Optional[Thumbnail] = None,
|
279
|
-
):
|
280
|
-
|
281
|
-
|
282
|
-
msg.append(StatelessFileSharing(xml=ET.fromstring(cache)))
|
293
|
+
) -> None:
|
294
|
+
if stored.sfs is not None:
|
295
|
+
msg.append(StatelessFileSharing(xml=ET.fromstring(stored.sfs)))
|
283
296
|
return
|
284
297
|
|
285
298
|
if not path:
|
286
299
|
return
|
287
300
|
|
288
|
-
sfs = self.xmpp["xep_0447"].get_sfs(
|
289
|
-
|
290
|
-
|
301
|
+
sfs = self.xmpp["xep_0447"].get_sfs(
|
302
|
+
path, [uploaded_url], attachment.content_type, attachment.caption
|
303
|
+
)
|
304
|
+
if attachment.name:
|
305
|
+
sfs["file"]["name"] = attachment.name
|
306
|
+
if attachment.disposition:
|
307
|
+
sfs["disposition"] = attachment.disposition
|
308
|
+
else:
|
309
|
+
del sfs["disposition"]
|
291
310
|
if thumbnail is not None:
|
292
311
|
sfs["file"].append(thumbnail)
|
293
|
-
|
294
|
-
|
312
|
+
stored.sfs = str(sfs)
|
295
313
|
msg.append(sfs)
|
296
314
|
|
297
315
|
def __send_url(
|
@@ -300,9 +318,9 @@ class AttachmentMixin(TextMessageMixin):
|
|
300
318
|
legacy_msg_id: LegacyMessageType,
|
301
319
|
uploaded_url: str,
|
302
320
|
caption: Optional[str] = None,
|
303
|
-
carbon=False,
|
321
|
+
carbon: bool = False,
|
304
322
|
when: Optional[datetime] = None,
|
305
|
-
correction=False,
|
323
|
+
correction: bool = False,
|
306
324
|
**kwargs,
|
307
325
|
) -> list[Message]:
|
308
326
|
msg["oob"]["url"] = uploaded_url
|
@@ -325,51 +343,21 @@ class AttachmentMixin(TextMessageMixin):
|
|
325
343
|
self._set_msg_id(msg, legacy_msg_id)
|
326
344
|
return [self._send(msg, carbon=carbon, **kwargs)]
|
327
345
|
|
328
|
-
|
346
|
+
def __get_base_message(
|
329
347
|
self,
|
330
|
-
file_path: Optional[Union[Path, str]] = None,
|
331
348
|
legacy_msg_id: Optional[LegacyMessageType] = None,
|
332
|
-
*,
|
333
|
-
async_data_stream: Optional[AsyncIterator[bytes]] = None,
|
334
|
-
data_stream: Optional[IO[bytes]] = None,
|
335
|
-
data: Optional[bytes] = None,
|
336
|
-
file_url: Optional[str] = None,
|
337
|
-
file_name: Optional[str] = None,
|
338
|
-
content_type: Optional[str] = None,
|
339
349
|
reply_to: Optional[MessageReference] = None,
|
340
350
|
when: Optional[datetime] = None,
|
341
|
-
caption: Optional[str] = None,
|
342
|
-
legacy_file_id: Optional[Union[str, int]] = None,
|
343
351
|
thread: Optional[LegacyThreadType] = None,
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
:param file_path: Path to the attachment
|
350
|
-
:param async_data_stream: Alternatively (and ideally) an AsyncIterator yielding bytes
|
351
|
-
:param data_stream: Alternatively, a stream of bytes (such as a File object)
|
352
|
-
:param data: Alternatively, a bytes object
|
353
|
-
:param file_url: Alternatively, a URL
|
354
|
-
:param file_name: How the file should be named.
|
355
|
-
:param content_type: MIME type, inferred from filename if not given
|
356
|
-
:param legacy_msg_id: If you want to be able to transport read markers from the gateway
|
357
|
-
user to the legacy network, specify this
|
358
|
-
:param reply_to: Quote another message (:xep:`0461`)
|
359
|
-
:param when: when the file was sent, for a "delay" tag (:xep:`0203`)
|
360
|
-
:param caption: an optional text that is linked to the file
|
361
|
-
:param legacy_file_id: A unique identifier for the file on the legacy network.
|
362
|
-
Plugins should try their best to provide it, to avoid duplicates.
|
363
|
-
:param thread:
|
364
|
-
"""
|
365
|
-
carbon = kwargs.pop("carbon", False)
|
366
|
-
mto = kwargs.pop("mto", None)
|
367
|
-
store_multi = kwargs.pop("store_multi", True)
|
368
|
-
correction = kwargs.get("correction", False)
|
352
|
+
carbon: bool = False,
|
353
|
+
correction: bool = False,
|
354
|
+
mto: Optional[JID] = None,
|
355
|
+
) -> Message:
|
369
356
|
if correction and (original_xmpp_id := self._legacy_to_xmpp(legacy_msg_id)):
|
370
|
-
|
371
|
-
self.
|
372
|
-
|
357
|
+
with self.xmpp.store.session() as orm:
|
358
|
+
xmpp_ids = self.xmpp.store.id_map.get_xmpp(
|
359
|
+
orm, self._recipient_pk(), str(legacy_msg_id), self.is_group
|
360
|
+
)
|
373
361
|
|
374
362
|
for xmpp_id in xmpp_ids:
|
375
363
|
if xmpp_id == original_xmpp_id:
|
@@ -386,7 +374,7 @@ class AttachmentMixin(TextMessageMixin):
|
|
386
374
|
else:
|
387
375
|
reply_to_for_attachment = reply_to
|
388
376
|
|
389
|
-
|
377
|
+
return self._make_message(
|
390
378
|
when=when,
|
391
379
|
reply_to=reply_to_for_attachment,
|
392
380
|
carbon=carbon,
|
@@ -394,43 +382,116 @@ class AttachmentMixin(TextMessageMixin):
|
|
394
382
|
thread=thread,
|
395
383
|
)
|
396
384
|
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
385
|
+
async def send_file(
|
386
|
+
self,
|
387
|
+
attachment: LegacyAttachment | Path | str,
|
388
|
+
legacy_msg_id: Optional[LegacyMessageType] = None,
|
389
|
+
*,
|
390
|
+
reply_to: Optional[MessageReference] = None,
|
391
|
+
when: Optional[datetime] = None,
|
392
|
+
thread: Optional[LegacyThreadType] = None,
|
393
|
+
**kwargs,
|
394
|
+
) -> tuple[Optional[str], list[Message]]:
|
395
|
+
"""
|
396
|
+
Send a single file from this :term:`XMPP Entity`.
|
397
|
+
|
398
|
+
:param attachment: The file to send.
|
399
|
+
Ideally, a :class:`.LegacyAttachment` with a unique ``legacy_file_id``
|
400
|
+
attribute set, to optimise potential future reuses.
|
401
|
+
It can also be:
|
402
|
+
- a :class:`pathlib.Path` instance to point to a local file, or
|
403
|
+
- a ``str``, representing a fetchable HTTP URL.
|
404
|
+
:param legacy_msg_id: If you want to be able to transport read markers from the gateway
|
405
|
+
user to the legacy network, specify this
|
406
|
+
:param reply_to: Quote another message (:xep:`0461`)
|
407
|
+
:param when: when the file was sent, for a "delay" tag (:xep:`0203`)
|
408
|
+
:param thread:
|
409
|
+
"""
|
410
|
+
coro = self.__send_file(
|
411
|
+
attachment,
|
412
|
+
legacy_msg_id,
|
413
|
+
reply_to=reply_to,
|
414
|
+
when=when,
|
415
|
+
thread=thread,
|
416
|
+
**kwargs,
|
409
417
|
)
|
418
|
+
if self.session is NotImplemented:
|
419
|
+
return await coro
|
420
|
+
elif not isinstance(attachment, LegacyAttachment):
|
421
|
+
return await coro
|
422
|
+
elif attachment.legacy_file_id is None:
|
423
|
+
return await coro
|
424
|
+
else:
|
425
|
+
# prevents race conditions where we download the same thing several time
|
426
|
+
# and end up attempting to insert it twice in the DB, raising an
|
427
|
+
# IntegrityError.
|
428
|
+
async with self.session.lock(("attachment", attachment.legacy_file_id)):
|
429
|
+
return await coro
|
410
430
|
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
431
|
+
async def __send_file(
|
432
|
+
self,
|
433
|
+
attachment: LegacyAttachment | Path | str,
|
434
|
+
legacy_msg_id: Optional[LegacyMessageType] = None,
|
435
|
+
*,
|
436
|
+
reply_to: Optional[MessageReference] = None,
|
437
|
+
when: Optional[datetime] = None,
|
438
|
+
thread: Optional[LegacyThreadType] = None,
|
439
|
+
**kwargs,
|
440
|
+
) -> tuple[Optional[str], list[Message]]:
|
441
|
+
store_multi = kwargs.pop("store_multi", True)
|
442
|
+
carbon = kwargs.pop("carbon", False)
|
443
|
+
mto = kwargs.pop("mto", None)
|
444
|
+
correction = kwargs.get("correction", False)
|
418
445
|
|
419
|
-
|
420
|
-
|
421
|
-
)
|
422
|
-
self.__set_sfs(
|
423
|
-
msg, new_url, local_path, content_type, caption, file_name, thumbnail
|
446
|
+
msg = self.__get_base_message(
|
447
|
+
legacy_msg_id, reply_to, when, thread, carbon, correction, mto
|
424
448
|
)
|
449
|
+
|
450
|
+
if isinstance(attachment, str):
|
451
|
+
attachment = LegacyAttachment(url=attachment)
|
452
|
+
elif isinstance(attachment, Path):
|
453
|
+
attachment = LegacyAttachment(path=attachment)
|
454
|
+
|
455
|
+
stored = await self.__get_stored(attachment)
|
456
|
+
|
457
|
+
if attachment.content_type is None and (
|
458
|
+
name := (attachment.name or attachment.url or attachment.path)
|
459
|
+
):
|
460
|
+
attachment.content_type, _ = guess_type(name)
|
461
|
+
|
462
|
+
if stored.url:
|
463
|
+
is_temp = False
|
464
|
+
local_path = None
|
465
|
+
new_url = stored.url
|
466
|
+
else:
|
467
|
+
is_temp, local_path, new_url = await self.__get_url(attachment, stored)
|
468
|
+
if new_url is None:
|
469
|
+
msg["body"] = (
|
470
|
+
"I tried to send a file, but something went wrong. "
|
471
|
+
"Tell your slidge admin to check the logs."
|
472
|
+
)
|
473
|
+
self._set_msg_id(msg, legacy_msg_id)
|
474
|
+
return None, [self._send(msg, **kwargs)]
|
475
|
+
|
476
|
+
stored.url = new_url
|
477
|
+
thumbnail = await self.__set_sims(msg, new_url, local_path, attachment, stored)
|
478
|
+
self.__set_sfs(msg, new_url, local_path, attachment, stored, thumbnail)
|
479
|
+
|
480
|
+
if self.session is not NotImplemented:
|
481
|
+
with self.xmpp.store.session(expire_on_commit=False) as orm:
|
482
|
+
orm.add(stored)
|
483
|
+
orm.commit()
|
484
|
+
|
425
485
|
if is_temp and isinstance(local_path, Path):
|
426
486
|
local_path.unlink()
|
427
487
|
local_path.parent.rmdir()
|
428
488
|
|
429
489
|
msgs = self.__send_url(
|
430
|
-
msg, legacy_msg_id, new_url, caption, carbon, when, **kwargs
|
490
|
+
msg, legacy_msg_id, new_url, attachment.caption, carbon, when, **kwargs
|
431
491
|
)
|
432
|
-
if
|
433
|
-
|
492
|
+
if self.session is not NotImplemented:
|
493
|
+
if store_multi:
|
494
|
+
self.__store_multi(legacy_msg_id, msgs)
|
434
495
|
return new_url, msgs
|
435
496
|
|
436
497
|
def __send_body(
|
@@ -463,18 +524,15 @@ class AttachmentMixin(TextMessageMixin):
|
|
463
524
|
reply_to: Optional[MessageReference] = None,
|
464
525
|
when: Optional[datetime] = None,
|
465
526
|
thread: Optional[LegacyThreadType] = None,
|
466
|
-
body_first=False,
|
467
|
-
correction=False,
|
527
|
+
body_first: bool = False,
|
528
|
+
correction: bool = False,
|
468
529
|
correction_event_id: Optional[LegacyMessageType] = None,
|
469
530
|
**kwargs,
|
470
|
-
):
|
531
|
+
) -> None:
|
471
532
|
# TODO: once the epic XEP-0385 vs XEP-0447 battle is over, pick
|
472
533
|
# one and stop sending several attachments this way
|
473
534
|
# we attach the legacy_message ID to the last message we send, because
|
474
535
|
# we don't want several messages with the same ID (especially for MUC MAM)
|
475
|
-
# TODO: refactor this so we limit the number of SQL calls, ie, if
|
476
|
-
# the legacy file ID is known, only fetch the row once, and if it
|
477
|
-
# is new, write it all in a single call
|
478
536
|
if not attachments and not body:
|
479
537
|
# ignoring empty message
|
480
538
|
return
|
@@ -500,18 +558,11 @@ class AttachmentMixin(TextMessageMixin):
|
|
500
558
|
else:
|
501
559
|
legacy = None
|
502
560
|
_url, msgs = await self.send_file(
|
503
|
-
|
504
|
-
|
505
|
-
file_url=attachment.url,
|
506
|
-
data_stream=attachment.stream,
|
507
|
-
data=attachment.data,
|
561
|
+
attachment,
|
562
|
+
legacy,
|
508
563
|
reply_to=reply_to,
|
509
564
|
when=when,
|
510
565
|
thread=thread,
|
511
|
-
file_name=attachment.name,
|
512
|
-
content_type=attachment.content_type,
|
513
|
-
legacy_file_id=attachment.legacy_file_id,
|
514
|
-
caption=attachment.caption,
|
515
566
|
store_multi=False,
|
516
567
|
**kwargs,
|
517
568
|
)
|
@@ -524,7 +575,7 @@ class AttachmentMixin(TextMessageMixin):
|
|
524
575
|
self,
|
525
576
|
legacy_msg_id: Optional[LegacyMessageType],
|
526
577
|
all_msgs: Sequence[Optional[Message]],
|
527
|
-
):
|
578
|
+
) -> None:
|
528
579
|
if legacy_msg_id is None:
|
529
580
|
return
|
530
581
|
ids = []
|
@@ -535,9 +586,11 @@ class AttachmentMixin(TextMessageMixin):
|
|
535
586
|
ids.append(stanza_id["id"])
|
536
587
|
else:
|
537
588
|
ids.append(msg.get_id())
|
538
|
-
self.xmpp.store.
|
539
|
-
self.
|
540
|
-
|
589
|
+
with self.xmpp.store.session() as orm:
|
590
|
+
self.xmpp.store.id_map.set_msg(
|
591
|
+
orm, self._recipient_pk(), str(legacy_msg_id), ids, self.is_group
|
592
|
+
)
|
593
|
+
orm.commit()
|
541
594
|
|
542
595
|
|
543
596
|
def get_thumbhash(path: Path) -> tuple[str, int, int]:
|