slidge 0.1.3__py3-none-any.whl → 0.2.0a1__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 +3 -5
- slidge/__main__.py +2 -196
- slidge/__version__.py +5 -0
- slidge/command/adhoc.py +8 -1
- slidge/command/admin.py +6 -7
- slidge/command/base.py +1 -2
- slidge/command/register.py +32 -16
- slidge/command/user.py +85 -6
- slidge/contact/contact.py +165 -49
- slidge/contact/roster.py +122 -47
- slidge/core/config.py +14 -11
- slidge/core/gateway/base.py +148 -36
- slidge/core/gateway/caps.py +7 -5
- slidge/core/gateway/disco.py +2 -4
- slidge/core/gateway/mam.py +1 -4
- slidge/core/gateway/muc_admin.py +1 -1
- slidge/core/gateway/ping.py +2 -3
- slidge/core/gateway/presence.py +1 -1
- slidge/core/gateway/registration.py +32 -21
- slidge/core/gateway/search.py +3 -5
- slidge/core/gateway/session_dispatcher.py +120 -57
- slidge/core/gateway/vcard_temp.py +7 -5
- slidge/core/mixins/__init__.py +11 -1
- slidge/core/mixins/attachment.py +32 -14
- slidge/core/mixins/avatar.py +90 -25
- slidge/core/mixins/base.py +8 -2
- slidge/core/mixins/db.py +18 -0
- slidge/core/mixins/disco.py +0 -10
- slidge/core/mixins/message.py +18 -8
- slidge/core/mixins/message_maker.py +17 -9
- slidge/core/mixins/presence.py +17 -4
- slidge/core/pubsub.py +54 -220
- slidge/core/session.py +69 -34
- slidge/db/__init__.py +4 -0
- slidge/db/alembic/env.py +64 -0
- slidge/db/alembic/script.py.mako +26 -0
- slidge/db/alembic/versions/09f27f098baa_add_missing_attributes_in_room.py +36 -0
- slidge/db/alembic/versions/2461390c0af2_store_contacts_caps_verstring_in_db.py +36 -0
- slidge/db/alembic/versions/29f5280c61aa_store_subject_setter_in_room.py +37 -0
- slidge/db/alembic/versions/2b1f45ab7379_store_room_subject_setter_by_nickname.py +41 -0
- slidge/db/alembic/versions/82a4af84b679_add_muc_history_filled.py +48 -0
- slidge/db/alembic/versions/8d2ced764698_rely_on_db_to_store_contacts_rooms_and_.py +133 -0
- slidge/db/alembic/versions/aa9d82a7f6ef_db_creation.py +85 -0
- slidge/db/alembic/versions/b33993e87db3_move_everything_to_persistent_db.py +214 -0
- slidge/db/alembic/versions/b64b1a793483_add_source_and_legacy_id_for_archived_.py +48 -0
- slidge/db/alembic/versions/c4a8ec35a0e8_per_room_user_nick.py +34 -0
- slidge/db/alembic/versions/e91195719c2c_store_users_avatars_persistently.py +26 -0
- slidge/db/avatar.py +235 -0
- slidge/db/meta.py +65 -0
- slidge/db/models.py +375 -0
- slidge/db/store.py +1078 -0
- slidge/group/archive.py +58 -14
- slidge/group/bookmarks.py +72 -57
- slidge/group/participant.py +87 -28
- slidge/group/room.py +369 -211
- slidge/main.py +201 -0
- slidge/migration.py +30 -0
- slidge/slixfix/__init__.py +35 -2
- slidge/slixfix/roster.py +11 -4
- slidge/slixfix/xep_0292/vcard4.py +3 -0
- slidge/util/archive_msg.py +2 -1
- slidge/util/db.py +1 -47
- slidge/util/test.py +71 -4
- slidge/util/types.py +29 -4
- slidge/util/util.py +22 -0
- {slidge-0.1.3.dist-info → slidge-0.2.0a1.dist-info}/METADATA +4 -4
- slidge-0.2.0a1.dist-info/RECORD +114 -0
- slidge/core/cache.py +0 -183
- slidge/util/schema.sql +0 -126
- slidge/util/sql.py +0 -508
- slidge-0.1.3.dist-info/RECORD +0 -96
- {slidge-0.1.3.dist-info → slidge-0.2.0a1.dist-info}/LICENSE +0 -0
- {slidge-0.1.3.dist-info → slidge-0.2.0a1.dist-info}/WHEEL +0 -0
- {slidge-0.1.3.dist-info → slidge-0.2.0a1.dist-info}/entry_points.txt +0 -0
@@ -9,7 +9,6 @@ from slixmpp.plugins.xep_0084.stanza import Info
|
|
9
9
|
|
10
10
|
from ... import LegacyContact
|
11
11
|
from ...group.room import LegacyMUC
|
12
|
-
from ...util.sql import db
|
13
12
|
from ...util.types import LinkPreview, Recipient, RecipientType
|
14
13
|
from ...util.util import (
|
15
14
|
dict_to_named_tuple,
|
@@ -62,6 +61,13 @@ class SessionDispatcher:
|
|
62
61
|
_exceptions_to_xmpp_errors(self.on_muc_owner_set), # type: ignore
|
63
62
|
)
|
64
63
|
)
|
64
|
+
xmpp.register_handler(
|
65
|
+
CoroutineCallback(
|
66
|
+
"ibr_remove",
|
67
|
+
StanzaPath("/iq/register"),
|
68
|
+
_exceptions_to_xmpp_errors(self.on_ibr_remove), # type: ignore
|
69
|
+
)
|
70
|
+
)
|
65
71
|
|
66
72
|
for event in (
|
67
73
|
"legacy_message",
|
@@ -157,7 +163,7 @@ class SessionDispatcher:
|
|
157
163
|
reply_fallback = None
|
158
164
|
if msg.get_plugin("reply", check=True):
|
159
165
|
try:
|
160
|
-
reply_to_msg_xmpp_id = _xmpp_msg_id_to_legacy(
|
166
|
+
reply_to_msg_xmpp_id = self._xmpp_msg_id_to_legacy(
|
161
167
|
session, msg["reply"]["id"]
|
162
168
|
)
|
163
169
|
except XMPPError:
|
@@ -170,7 +176,7 @@ class SessionDispatcher:
|
|
170
176
|
else:
|
171
177
|
reply_to_jid = JID(msg["reply"]["to"])
|
172
178
|
if msg["type"] == "chat":
|
173
|
-
if reply_to_jid.bare != session.
|
179
|
+
if reply_to_jid.bare != session.user_jid.bare:
|
174
180
|
try:
|
175
181
|
reply_to = await session.contacts.by_jid(reply_to_jid)
|
176
182
|
except XMPPError:
|
@@ -242,13 +248,17 @@ class SessionDispatcher:
|
|
242
248
|
if isinstance(e, LegacyMUC):
|
243
249
|
await e.echo(msg, legacy_msg_id)
|
244
250
|
if legacy_msg_id is not None:
|
245
|
-
|
251
|
+
self.xmpp.store.sent.set_group_message(
|
252
|
+
session.user_pk, legacy_msg_id, msg.get_id()
|
253
|
+
)
|
246
254
|
else:
|
247
255
|
self.__ack(msg)
|
248
256
|
if legacy_msg_id is not None:
|
249
|
-
|
257
|
+
self.xmpp.store.sent.set_message(
|
258
|
+
session.user_pk, legacy_msg_id, msg.get_id()
|
259
|
+
)
|
250
260
|
if session.MESSAGE_IDS_ARE_THREAD_IDS and (t := msg["thread"]):
|
251
|
-
session.
|
261
|
+
self.xmpp.store.sent.set_thread(session.user_pk, t, legacy_msg_id)
|
252
262
|
|
253
263
|
async def on_groupchat_message(self, msg: Message):
|
254
264
|
await self.on_legacy_message(msg)
|
@@ -257,9 +267,11 @@ class SessionDispatcher:
|
|
257
267
|
session, entity, thread = await self.__get_session_entity_thread(msg)
|
258
268
|
xmpp_id = msg["replace"]["id"]
|
259
269
|
if isinstance(entity, LegacyMUC):
|
260
|
-
legacy_id =
|
270
|
+
legacy_id = self.xmpp.store.sent.get_group_legacy_id(
|
271
|
+
session.user_pk, xmpp_id
|
272
|
+
)
|
261
273
|
else:
|
262
|
-
legacy_id = _xmpp_msg_id_to_legacy(session, xmpp_id)
|
274
|
+
legacy_id = self._xmpp_msg_id_to_legacy(session, xmpp_id)
|
263
275
|
|
264
276
|
if isinstance(entity, LegacyMUC):
|
265
277
|
mentions = await entity.parse_mentions(msg["body"])
|
@@ -323,12 +335,16 @@ class SessionDispatcher:
|
|
323
335
|
|
324
336
|
if isinstance(entity, LegacyMUC):
|
325
337
|
if new_legacy_msg_id is not None:
|
326
|
-
|
338
|
+
self.xmpp.store.sent.set_group_message(
|
339
|
+
session.user_pk, new_legacy_msg_id, msg.get_id()
|
340
|
+
)
|
327
341
|
await entity.echo(msg, new_legacy_msg_id)
|
328
342
|
else:
|
329
343
|
self.__ack(msg)
|
330
344
|
if new_legacy_msg_id is not None:
|
331
|
-
|
345
|
+
self.xmpp.store.sent.set_message(
|
346
|
+
session.user_pk, new_legacy_msg_id, msg.get_id()
|
347
|
+
)
|
332
348
|
|
333
349
|
async def on_message_retract(self, msg: Message):
|
334
350
|
session, entity, thread = await self.__get_session_entity_thread(msg)
|
@@ -338,7 +354,7 @@ class SessionDispatcher:
|
|
338
354
|
"This legacy service does not support message retraction.",
|
339
355
|
)
|
340
356
|
xmpp_id: str = msg["retract"]["id"]
|
341
|
-
legacy_id = _xmpp_msg_id_to_legacy(session, xmpp_id)
|
357
|
+
legacy_id = self._xmpp_msg_id_to_legacy(session, xmpp_id)
|
342
358
|
if legacy_id:
|
343
359
|
await session.on_retract(entity, legacy_id, thread=thread)
|
344
360
|
if isinstance(entity, LegacyMUC):
|
@@ -362,7 +378,7 @@ class SessionDispatcher:
|
|
362
378
|
to_mark = [displayed_msg_id]
|
363
379
|
for xmpp_id in to_mark:
|
364
380
|
await session.on_displayed(
|
365
|
-
e, _xmpp_msg_id_to_legacy(session, xmpp_id), legacy_thread
|
381
|
+
e, self._xmpp_msg_id_to_legacy(session, xmpp_id), legacy_thread
|
366
382
|
)
|
367
383
|
if isinstance(e, LegacyMUC):
|
368
384
|
await e.echo(msg, None)
|
@@ -397,7 +413,7 @@ class SessionDispatcher:
|
|
397
413
|
if special_msg:
|
398
414
|
legacy_id = react_to
|
399
415
|
else:
|
400
|
-
legacy_id = _xmpp_msg_id_to_legacy(session, react_to)
|
416
|
+
legacy_id = self._xmpp_msg_id_to_legacy(session, react_to)
|
401
417
|
|
402
418
|
if not legacy_id:
|
403
419
|
log.debug("Ignored reaction from user")
|
@@ -434,9 +450,10 @@ class SessionDispatcher:
|
|
434
450
|
else:
|
435
451
|
self.__ack(msg)
|
436
452
|
|
437
|
-
multi =
|
453
|
+
multi = self.xmpp.store.multi.get_xmpp_ids(session.user_pk, react_to)
|
438
454
|
if not multi:
|
439
455
|
return
|
456
|
+
multi = [m for m in multi if react_to != m]
|
440
457
|
|
441
458
|
if isinstance(entity, LegacyMUC):
|
442
459
|
for xmpp_id in multi:
|
@@ -461,14 +478,17 @@ class SessionDispatcher:
|
|
461
478
|
|
462
479
|
pto = p.get_to()
|
463
480
|
if pto == self.xmpp.boundjid.bare:
|
464
|
-
|
465
|
-
# a presence show if available. Weird, weird, weird slix.
|
481
|
+
session.log.debug("Received a presence from %s", p.get_from())
|
466
482
|
if (ptype := p.get_type()) not in _USEFUL_PRESENCES:
|
467
483
|
return
|
484
|
+
if not session.user.preferences.get("sync_presence", False):
|
485
|
+
session.log.debug("User does not want to sync their presence")
|
486
|
+
return
|
487
|
+
# NB: get_type() returns either a proper presence type or
|
488
|
+
# a presence show if available. Weird, weird, weird slix.
|
468
489
|
resources = self.xmpp.roster[self.xmpp.boundjid.bare][
|
469
490
|
p.get_from()
|
470
491
|
].resources
|
471
|
-
session.log.debug("Received a presence from %s", p.get_from())
|
472
492
|
await session.on_presence(
|
473
493
|
p.get_from().resource,
|
474
494
|
ptype, # type: ignore
|
@@ -478,8 +498,12 @@ class SessionDispatcher:
|
|
478
498
|
)
|
479
499
|
return
|
480
500
|
|
481
|
-
muc = session.bookmarks.
|
482
|
-
|
501
|
+
muc = session.bookmarks.by_jid_only_if_exists(JID(pto.bare))
|
502
|
+
|
503
|
+
if muc is not None and p.get_type() == "unavailable":
|
504
|
+
return muc.on_presence_unavailable(p)
|
505
|
+
|
506
|
+
if muc is None or p.get_from().resource not in muc.get_user_resources():
|
483
507
|
return
|
484
508
|
|
485
509
|
if pto.resource == muc.user_nick:
|
@@ -530,29 +554,36 @@ class SessionDispatcher:
|
|
530
554
|
return
|
531
555
|
|
532
556
|
stanza_id = msg["pubsub_event"]["items"]["item"]["displayed"]["stanza_id"]["id"]
|
533
|
-
await session.on_displayed(
|
557
|
+
await session.on_displayed(
|
558
|
+
chat, self._xmpp_msg_id_to_legacy(session, stanza_id)
|
559
|
+
)
|
534
560
|
|
535
561
|
async def on_avatar_metadata_publish(self, m: Message):
|
536
|
-
if not config.SYNC_AVATAR:
|
537
|
-
return
|
538
|
-
|
539
562
|
session = await self.__get_session(m, timeout=None)
|
563
|
+
if not session.user.preferences.get("sync_avatar", False):
|
564
|
+
session.log.debug("User does not want to sync their avatar")
|
565
|
+
return
|
540
566
|
info = m["pubsub_event"]["items"]["item"]["avatar_metadata"]["info"]
|
541
567
|
|
542
568
|
await self.on_avatar_metadata_info(session, info)
|
543
569
|
|
544
570
|
async def on_avatar_metadata_info(self, session: BaseSession, info: Info):
|
545
|
-
session.log.debug("Avatar metadata info: %s", info)
|
546
571
|
hash_ = info["id"]
|
547
572
|
|
548
|
-
if session.avatar_hash == hash_:
|
573
|
+
if session.user.avatar_hash == hash_:
|
574
|
+
session.log.debug("We already know this avatar hash")
|
549
575
|
return
|
550
|
-
session
|
576
|
+
with self.xmpp.store.session() as orm_session:
|
577
|
+
user = self.xmpp.store.users.get(session.user_jid)
|
578
|
+
assert user is not None
|
579
|
+
user.avatar_hash = hash_
|
580
|
+
orm_session.add(user)
|
581
|
+
orm_session.commit()
|
551
582
|
|
552
583
|
if hash_:
|
553
584
|
try:
|
554
585
|
iq = await self.xmpp.plugin["xep_0084"].retrieve_avatar(
|
555
|
-
session.
|
586
|
+
session.user_jid, hash_, ifrom=self.xmpp.boundjid.bare
|
556
587
|
)
|
557
588
|
except IqError as e:
|
558
589
|
session.log.warning("Could not fetch the user's avatar: %s", e)
|
@@ -593,7 +624,7 @@ class SessionDispatcher:
|
|
593
624
|
"Slidge only implements moderation/retraction",
|
594
625
|
)
|
595
626
|
|
596
|
-
legacy_id = _xmpp_msg_id_to_legacy(session, xmpp_id)
|
627
|
+
legacy_id = self._xmpp_msg_id_to_legacy(session, xmpp_id)
|
597
628
|
await session.on_moderate(muc, legacy_id, moderate["reason"] or None)
|
598
629
|
iq.reply(clear=True).send()
|
599
630
|
|
@@ -601,14 +632,28 @@ class SessionDispatcher:
|
|
601
632
|
session = await self.__get_session(iq)
|
602
633
|
session.raise_if_not_logged()
|
603
634
|
|
604
|
-
item = iq["mucadmin_query"]["item"]
|
605
|
-
contact = await session.contacts.by_jid(JID(item["jid"]))
|
606
|
-
|
607
635
|
muc = await session.bookmarks.by_jid(iq.get_to())
|
608
636
|
|
609
|
-
|
610
|
-
|
611
|
-
|
637
|
+
item = iq["mucadmin_query"]["item"]
|
638
|
+
if item["jid"]:
|
639
|
+
contact = await session.contacts.by_jid(JID(item["jid"]))
|
640
|
+
else:
|
641
|
+
part = await muc.get_participant(
|
642
|
+
item["nick"], fill_first=True, raise_if_not_found=True
|
643
|
+
)
|
644
|
+
assert part.contact is not None
|
645
|
+
contact = part.contact
|
646
|
+
|
647
|
+
if item["affiliation"]:
|
648
|
+
await muc.on_set_affiliation(
|
649
|
+
contact,
|
650
|
+
item["affiliation"],
|
651
|
+
item["reason"] or None,
|
652
|
+
item["nick"] or None,
|
653
|
+
)
|
654
|
+
elif item["role"] == "none":
|
655
|
+
await muc.on_kick(contact, item["reason"] or None)
|
656
|
+
|
612
657
|
iq.reply(clear=True).send()
|
613
658
|
|
614
659
|
async def on_groupchat_direct_invite(self, msg: Message):
|
@@ -718,25 +763,39 @@ class SessionDispatcher:
|
|
718
763
|
)
|
719
764
|
await muc.on_set_subject(msg["subject"])
|
720
765
|
|
766
|
+
async def on_ibr_remove(self, iq: Iq):
|
767
|
+
if iq.get_to() == self.xmpp.boundjid.bare:
|
768
|
+
return
|
721
769
|
|
722
|
-
|
723
|
-
|
724
|
-
if sent:
|
725
|
-
return sent
|
770
|
+
session = await self.__get_session(iq)
|
771
|
+
session.raise_if_not_logged()
|
726
772
|
|
727
|
-
|
728
|
-
|
729
|
-
|
773
|
+
if iq["type"] == "set" and iq["register"]["remove"]:
|
774
|
+
muc = await session.bookmarks.by_jid(iq.get_to())
|
775
|
+
await session.on_leave_group(muc.legacy_id)
|
776
|
+
iq.reply().send()
|
777
|
+
return
|
730
778
|
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
779
|
+
raise XMPPError("feature-not-implemented")
|
780
|
+
|
781
|
+
def _xmpp_msg_id_to_legacy(self, session: "BaseSession", xmpp_id: str):
|
782
|
+
sent = self.xmpp.store.sent.get_legacy_id(session.user_pk, xmpp_id)
|
783
|
+
if sent is not None:
|
784
|
+
return self.xmpp.LEGACY_MSG_ID_TYPE(sent)
|
785
|
+
|
786
|
+
multi = self.xmpp.store.multi.get_legacy_id(session.user_pk, xmpp_id)
|
787
|
+
if multi:
|
788
|
+
return self.xmpp.LEGACY_MSG_ID_TYPE(multi)
|
789
|
+
|
790
|
+
try:
|
791
|
+
return session.xmpp_to_legacy_msg_id(xmpp_id)
|
792
|
+
except XMPPError:
|
793
|
+
raise
|
794
|
+
except Exception as e:
|
795
|
+
log.debug("Couldn't convert xmpp msg ID to legacy ID.", exc_info=e)
|
796
|
+
raise XMPPError(
|
797
|
+
"internal-server-error", "Couldn't convert xmpp msg ID to legacy ID."
|
798
|
+
)
|
740
799
|
|
741
800
|
|
742
801
|
def _ignore(session: "BaseSession", msg: Message):
|
@@ -755,14 +814,18 @@ async def _xmpp_to_legacy_thread(
|
|
755
814
|
return
|
756
815
|
|
757
816
|
if session.MESSAGE_IDS_ARE_THREAD_IDS:
|
758
|
-
return session.
|
817
|
+
return session.xmpp.store.sent.get_legacy_thread(session.user_pk, xmpp_thread)
|
759
818
|
|
760
819
|
async with session.thread_creation_lock:
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
820
|
+
legacy_thread_str = session.xmpp.store.sent.get_legacy_thread(
|
821
|
+
session.user_pk, xmpp_thread
|
822
|
+
)
|
823
|
+
if legacy_thread_str is None:
|
824
|
+
legacy_thread = str(await recipient.create_thread(xmpp_thread))
|
825
|
+
session.xmpp.store.sent.set_thread(
|
826
|
+
session.user_pk, xmpp_thread, legacy_thread
|
827
|
+
)
|
828
|
+
return session.xmpp.LEGACY_MSG_ID_TYPE(legacy_thread)
|
766
829
|
|
767
830
|
|
768
831
|
async def _get_entity(session: "BaseSession", m: Message) -> RecipientType:
|
@@ -770,7 +833,7 @@ async def _get_entity(session: "BaseSession", m: Message) -> RecipientType:
|
|
770
833
|
if m.get_type() == "groupchat":
|
771
834
|
muc = await session.bookmarks.by_jid(m.get_to())
|
772
835
|
r = m.get_from().resource
|
773
|
-
if r not in muc.
|
836
|
+
if r not in muc.get_user_resources():
|
774
837
|
session.create_task(muc.kick_resource(r))
|
775
838
|
raise XMPPError("not-acceptable", "You are not connected to this chat")
|
776
839
|
return muc
|
@@ -40,11 +40,13 @@ class VCardTemp:
|
|
40
40
|
return await self.__handle_set_vcard_temp(iq)
|
41
41
|
|
42
42
|
async def __fetch_user_avatar(self, session: BaseSession):
|
43
|
-
hash_ = session.avatar_hash
|
43
|
+
hash_ = session.user.avatar_hash
|
44
44
|
if not hash_:
|
45
|
-
raise XMPPError(
|
45
|
+
raise XMPPError(
|
46
|
+
"item-not-found", "The slidge user does not have any avatar set"
|
47
|
+
)
|
46
48
|
meta_iq = await self.xmpp.plugin["xep_0060"].get_item(
|
47
|
-
session.
|
49
|
+
session.user_jid,
|
48
50
|
MetaData.namespace,
|
49
51
|
hash_,
|
50
52
|
ifrom=self.xmpp.boundjid.bare,
|
@@ -52,14 +54,14 @@ class VCardTemp:
|
|
52
54
|
info = meta_iq["pubsub"]["items"]["item"]["avatar_metadata"]["info"]
|
53
55
|
type_ = info["type"]
|
54
56
|
data_iq = await self.xmpp.plugin["xep_0084"].retrieve_avatar(
|
55
|
-
session.
|
57
|
+
session.user_jid, hash_, ifrom=self.xmpp.boundjid.bare
|
56
58
|
)
|
57
59
|
bytes_ = data_iq["pubsub"]["items"]["item"]["avatar_data"]["value"]
|
58
60
|
return bytes_, type_
|
59
61
|
|
60
62
|
async def __handle_get_vcard_temp(self, iq: Iq):
|
61
63
|
session = self.xmpp.get_session_from_stanza(iq)
|
62
|
-
entity = await session.get_contact_or_group_or_participant(iq.get_to())
|
64
|
+
entity = await session.get_contact_or_group_or_participant(iq.get_to(), False)
|
63
65
|
if not entity:
|
64
66
|
raise XMPPError("item-not-found")
|
65
67
|
|
slidge/core/mixins/__init__.py
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
Mixins
|
3
3
|
"""
|
4
4
|
|
5
|
+
from typing import Optional
|
6
|
+
|
5
7
|
from .avatar import AvatarMixin
|
6
8
|
from .disco import ChatterDiscoMixin
|
7
9
|
from .message import MessageCarbonMixin, MessageMixin
|
@@ -16,4 +18,12 @@ class FullCarbonMixin(ChatterDiscoMixin, MessageCarbonMixin, PresenceMixin):
|
|
16
18
|
pass
|
17
19
|
|
18
20
|
|
19
|
-
|
21
|
+
class StoredAttributeMixin:
|
22
|
+
def serialize_extra_attributes(self) -> Optional[dict]:
|
23
|
+
return None
|
24
|
+
|
25
|
+
def deserialize_extra_attributes(self, data: dict) -> None:
|
26
|
+
pass
|
27
|
+
|
28
|
+
|
29
|
+
__all__ = ("AvatarMixin", "FullCarbonMixin", "StoredAttributeMixin")
|
slidge/core/mixins/attachment.py
CHANGED
@@ -9,7 +9,7 @@ import warnings
|
|
9
9
|
from datetime import datetime
|
10
10
|
from mimetypes import guess_type
|
11
11
|
from pathlib import Path
|
12
|
-
from typing import IO, Collection, Optional, Sequence, Union
|
12
|
+
from typing import IO, AsyncIterator, Collection, Optional, Sequence, Union
|
13
13
|
from urllib.parse import quote as urlquote
|
14
14
|
from uuid import uuid4
|
15
15
|
from xml.etree import ElementTree as ET
|
@@ -22,7 +22,7 @@ from slixmpp.plugins.xep_0363 import FileUploadError
|
|
22
22
|
from slixmpp.plugins.xep_0385.stanza import Sims
|
23
23
|
from slixmpp.plugins.xep_0447.stanza import StatelessFileSharing
|
24
24
|
|
25
|
-
from ...
|
25
|
+
from ...db.avatar import avatar_cache
|
26
26
|
from ...util.types import (
|
27
27
|
LegacyAttachment,
|
28
28
|
LegacyMessageType,
|
@@ -31,11 +31,14 @@ from ...util.types import (
|
|
31
31
|
)
|
32
32
|
from ...util.util import fix_suffix
|
33
33
|
from .. import config
|
34
|
-
from ..cache import avatar_cache
|
35
34
|
from .message_maker import MessageMaker
|
36
35
|
|
37
36
|
|
38
37
|
class AttachmentMixin(MessageMaker):
|
38
|
+
def __init__(self, *a, **kw):
|
39
|
+
super().__init__(*a, **kw)
|
40
|
+
self.__store = self.xmpp.store.attachments
|
41
|
+
|
39
42
|
def send_text(self, *_, **k) -> Optional[Message]:
|
40
43
|
raise NotImplementedError
|
41
44
|
|
@@ -138,6 +141,7 @@ class AttachmentMixin(MessageMaker):
|
|
138
141
|
async def __get_url(
|
139
142
|
self,
|
140
143
|
file_path: Optional[Path] = None,
|
144
|
+
async_data_stream: Optional[AsyncIterator[bytes]] = None,
|
141
145
|
data_stream: Optional[IO[bytes]] = None,
|
142
146
|
data: Optional[bytes] = None,
|
143
147
|
file_url: Optional[str] = None,
|
@@ -146,13 +150,13 @@ class AttachmentMixin(MessageMaker):
|
|
146
150
|
legacy_file_id: Optional[Union[str, int]] = None,
|
147
151
|
) -> tuple[bool, Optional[Path], str]:
|
148
152
|
if legacy_file_id:
|
149
|
-
cache =
|
153
|
+
cache = self.__store.get_url(str(legacy_file_id))
|
150
154
|
if cache is not None:
|
151
155
|
async with self.session.http.head(cache) as r:
|
152
156
|
if r.status < 400:
|
153
157
|
return False, None, cache
|
154
158
|
else:
|
155
|
-
|
159
|
+
self.__store.remove(str(legacy_file_id))
|
156
160
|
|
157
161
|
if file_url and config.USE_ATTACHMENT_ORIGINAL_URLS:
|
158
162
|
return False, None, file_url
|
@@ -173,14 +177,23 @@ class AttachmentMixin(MessageMaker):
|
|
173
177
|
with file_path.open("wb") as f:
|
174
178
|
f.write(await r.read())
|
175
179
|
|
176
|
-
|
177
|
-
|
178
|
-
data = data_stream.read()
|
180
|
+
elif data_stream is not None:
|
181
|
+
data = data_stream.read()
|
179
182
|
if data is None:
|
180
183
|
raise RuntimeError
|
181
184
|
|
182
185
|
with file_path.open("wb") as f:
|
183
186
|
f.write(data)
|
187
|
+
elif async_data_stream is not None:
|
188
|
+
# TODO: patch slixmpp to allow this as data source for
|
189
|
+
# upload_file() so we don't even have to write anything
|
190
|
+
# to disk.
|
191
|
+
with file_path.open("wb") as f:
|
192
|
+
async for chunk in async_data_stream:
|
193
|
+
f.write(chunk)
|
194
|
+
elif data is not None:
|
195
|
+
with file_path.open("wb") as f:
|
196
|
+
f.write(data)
|
184
197
|
|
185
198
|
is_temp = not bool(config.NO_UPLOAD_PATH)
|
186
199
|
else:
|
@@ -198,7 +211,7 @@ class AttachmentMixin(MessageMaker):
|
|
198
211
|
local_path = file_path
|
199
212
|
new_url = await self.__upload(file_path, file_name, content_type)
|
200
213
|
if legacy_file_id:
|
201
|
-
|
214
|
+
self.__store.set_url(self.session.user_pk, str(legacy_file_id), new_url)
|
202
215
|
|
203
216
|
return is_temp, local_path, new_url
|
204
217
|
|
@@ -211,7 +224,7 @@ class AttachmentMixin(MessageMaker):
|
|
211
224
|
caption: Optional[str] = None,
|
212
225
|
file_name: Optional[str] = None,
|
213
226
|
):
|
214
|
-
cache =
|
227
|
+
cache = self.__store.get_sims(uploaded_url)
|
215
228
|
if cache:
|
216
229
|
msg.append(Sims(xml=ET.fromstring(cache)))
|
217
230
|
return
|
@@ -238,7 +251,7 @@ class AttachmentMixin(MessageMaker):
|
|
238
251
|
thumbnail["media-type"] = "image/blurhash"
|
239
252
|
thumbnail["uri"] = "data:image/blurhash," + urlquote(h)
|
240
253
|
|
241
|
-
|
254
|
+
self.__store.set_sims(uploaded_url, str(sims))
|
242
255
|
|
243
256
|
msg.append(sims)
|
244
257
|
|
@@ -251,7 +264,7 @@ class AttachmentMixin(MessageMaker):
|
|
251
264
|
caption: Optional[str] = None,
|
252
265
|
file_name: Optional[str] = None,
|
253
266
|
):
|
254
|
-
cache =
|
267
|
+
cache = self.__store.get_sfs(uploaded_url)
|
255
268
|
if cache:
|
256
269
|
msg.append(StatelessFileSharing(xml=ET.fromstring(cache)))
|
257
270
|
return
|
@@ -262,7 +275,7 @@ class AttachmentMixin(MessageMaker):
|
|
262
275
|
sfs = self.xmpp["xep_0447"].get_sfs(path, [uploaded_url], content_type, caption)
|
263
276
|
if file_name:
|
264
277
|
sfs["file"]["name"] = file_name
|
265
|
-
|
278
|
+
self.__store.set_sfs(uploaded_url, str(sfs))
|
266
279
|
|
267
280
|
msg.append(sfs)
|
268
281
|
|
@@ -293,6 +306,7 @@ class AttachmentMixin(MessageMaker):
|
|
293
306
|
file_path: Optional[Union[Path, str]] = None,
|
294
307
|
legacy_msg_id: Optional[LegacyMessageType] = None,
|
295
308
|
*,
|
309
|
+
async_data_stream: Optional[AsyncIterator[bytes]] = None,
|
296
310
|
data_stream: Optional[IO[bytes]] = None,
|
297
311
|
data: Optional[bytes] = None,
|
298
312
|
file_url: Optional[str] = None,
|
@@ -309,6 +323,7 @@ class AttachmentMixin(MessageMaker):
|
|
309
323
|
Send a single file from this :term:`XMPP Entity`.
|
310
324
|
|
311
325
|
:param file_path: Path to the attachment
|
326
|
+
:param async_data_stream: Alternatively (and ideally) an AsyncIterator yielding bytes
|
312
327
|
:param data_stream: Alternatively, a stream of bytes (such as a File object)
|
313
328
|
:param data: Alternatively, a bytes object
|
314
329
|
:param file_url: Alternatively, a URL
|
@@ -339,6 +354,7 @@ class AttachmentMixin(MessageMaker):
|
|
339
354
|
|
340
355
|
is_temp, local_path, new_url = await self.__get_url(
|
341
356
|
Path(file_path) if file_path else None,
|
357
|
+
async_data_stream,
|
342
358
|
data_stream,
|
343
359
|
data,
|
344
360
|
file_url,
|
@@ -472,7 +488,9 @@ class AttachmentMixin(MessageMaker):
|
|
472
488
|
ids.append(stanza_id["id"])
|
473
489
|
else:
|
474
490
|
ids.append(msg.get_id())
|
475
|
-
|
491
|
+
self.xmpp.store.multi.set_xmpp_ids(
|
492
|
+
self.session.user_pk, str(legacy_msg_id), ids
|
493
|
+
)
|
476
494
|
|
477
495
|
|
478
496
|
def get_blurhash(path: Path, n=9) -> tuple[str, int, int]:
|