slidge 0.2.0a0__py3-none-any.whl → 0.2.0a1__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- slidge/__version__.py +1 -1
- slidge/command/admin.py +1 -1
- slidge/command/user.py +0 -1
- slidge/contact/contact.py +86 -32
- slidge/contact/roster.py +79 -19
- slidge/core/config.py +1 -4
- slidge/core/gateway/base.py +9 -2
- slidge/core/gateway/caps.py +7 -5
- slidge/core/gateway/muc_admin.py +1 -1
- slidge/core/gateway/session_dispatcher.py +20 -6
- slidge/core/gateway/vcard_temp.py +1 -1
- slidge/core/mixins/attachment.py +17 -4
- slidge/core/mixins/avatar.py +26 -9
- slidge/core/mixins/db.py +18 -0
- slidge/core/mixins/disco.py +0 -10
- slidge/core/mixins/message.py +7 -1
- slidge/core/mixins/presence.py +6 -3
- slidge/core/pubsub.py +7 -15
- slidge/core/session.py +6 -3
- slidge/db/alembic/versions/2461390c0af2_store_contacts_caps_verstring_in_db.py +36 -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/aa9d82a7f6ef_db_creation.py +11 -2
- 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/avatar.py +20 -9
- slidge/db/models.py +16 -6
- slidge/db/store.py +217 -115
- slidge/group/archive.py +46 -1
- slidge/group/bookmarks.py +17 -5
- slidge/group/participant.py +10 -3
- slidge/group/room.py +183 -125
- slidge/main.py +3 -3
- slidge/slixfix/xep_0292/vcard4.py +2 -0
- slidge/util/archive_msg.py +2 -1
- slidge/util/test.py +54 -4
- slidge/util/types.py +5 -0
- slidge/util/util.py +22 -0
- {slidge-0.2.0a0.dist-info → slidge-0.2.0a1.dist-info}/METADATA +2 -4
- {slidge-0.2.0a0.dist-info → slidge-0.2.0a1.dist-info}/RECORD +43 -37
- {slidge-0.2.0a0.dist-info → slidge-0.2.0a1.dist-info}/LICENSE +0 -0
- {slidge-0.2.0a0.dist-info → slidge-0.2.0a1.dist-info}/WHEEL +0 -0
- {slidge-0.2.0a0.dist-info → slidge-0.2.0a1.dist-info}/entry_points.txt +0 -0
slidge/db/store.py
CHANGED
@@ -2,7 +2,6 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
import json
|
4
4
|
import logging
|
5
|
-
import warnings
|
6
5
|
from contextlib import contextmanager
|
7
6
|
from datetime import datetime, timedelta, timezone
|
8
7
|
from typing import TYPE_CHECKING, Collection, Iterator, Optional, Type
|
@@ -11,6 +10,7 @@ from slixmpp import JID, Iq, Message, Presence
|
|
11
10
|
from slixmpp.exceptions import XMPPError
|
12
11
|
from sqlalchemy import Engine, delete, select, update
|
13
12
|
from sqlalchemy.orm import Session, attributes
|
13
|
+
from sqlalchemy.sql.functions import count
|
14
14
|
|
15
15
|
from ..util.archive_msg import HistoryMessage
|
16
16
|
from ..util.types import URL, CachedPresence
|
@@ -19,6 +19,7 @@ from ..util.types import MamMetadata, MucAffiliation, MucRole
|
|
19
19
|
from .meta import Base
|
20
20
|
from .models import (
|
21
21
|
ArchivedMessage,
|
22
|
+
ArchivedMessageSource,
|
22
23
|
Attachment,
|
23
24
|
Avatar,
|
24
25
|
Contact,
|
@@ -63,9 +64,18 @@ class UpdatedMixin(EngineMixin):
|
|
63
64
|
def __init__(self, *a, **kw):
|
64
65
|
super().__init__(*a, **kw)
|
65
66
|
with self.session() as session:
|
67
|
+
session.execute(
|
68
|
+
delete(self.model).where(~self.model.updated) # type:ignore
|
69
|
+
)
|
66
70
|
session.execute(update(self.model).values(updated=False)) # type:ignore
|
67
71
|
session.commit()
|
68
72
|
|
73
|
+
def get_by_pk(self, pk: int) -> Optional[Base]:
|
74
|
+
with self.session() as session:
|
75
|
+
return session.execute(
|
76
|
+
select(self.model).where(self.model.id == pk) # type:ignore
|
77
|
+
).scalar()
|
78
|
+
|
69
79
|
|
70
80
|
class SlidgeStore(EngineMixin):
|
71
81
|
def __init__(self, engine: Engine):
|
@@ -134,34 +144,18 @@ class AvatarStore(EngineMixin):
|
|
134
144
|
select(Avatar).where(Avatar.legacy_id == legacy_id)
|
135
145
|
).scalar()
|
136
146
|
|
137
|
-
def
|
147
|
+
def get_by_pk(self, pk: int) -> Optional[Avatar]:
|
138
148
|
with self.session() as session:
|
139
|
-
|
140
|
-
if pk is None:
|
141
|
-
warnings.warn("avatar not found")
|
142
|
-
return
|
143
|
-
session.execute(
|
144
|
-
update(Contact).where(Contact.jid == jid.bare).values(avatar_id=pk)
|
145
|
-
)
|
146
|
-
session.execute(
|
147
|
-
update(Room).where(Room.jid == jid.bare).values(avatar_id=pk)
|
148
|
-
)
|
149
|
-
session.commit()
|
149
|
+
return session.execute(select(Avatar).where(Avatar.id == pk)).scalar()
|
150
150
|
|
151
|
-
def
|
151
|
+
def delete_by_pk(self, pk: int):
|
152
152
|
with self.session() as session:
|
153
|
-
|
154
|
-
|
155
|
-
).scalar()
|
156
|
-
if avatar is not None:
|
157
|
-
return avatar
|
158
|
-
return session.execute(
|
159
|
-
select(Avatar).where(Avatar.rooms.any(Room.jid == jid))
|
160
|
-
).scalar()
|
153
|
+
session.execute(delete(Avatar).where(Avatar.id == pk))
|
154
|
+
session.commit()
|
161
155
|
|
162
|
-
def
|
156
|
+
def get_all(self) -> Iterator[Avatar]:
|
163
157
|
with self.session() as session:
|
164
|
-
|
158
|
+
yield from session.execute(select(Avatar)).scalars()
|
165
159
|
|
166
160
|
|
167
161
|
class SentStore(EngineMixin):
|
@@ -273,22 +267,6 @@ class ContactStore(UpdatedMixin):
|
|
273
267
|
session.execute(update(Contact).values(cached_presence=False))
|
274
268
|
session.commit()
|
275
269
|
|
276
|
-
def add(self, user_pk: int, legacy_id: str, contact_jid: JID) -> int:
|
277
|
-
with self.session() as session:
|
278
|
-
existing = session.execute(
|
279
|
-
select(Contact)
|
280
|
-
.where(Contact.legacy_id == legacy_id)
|
281
|
-
.where(Contact.jid == contact_jid.bare)
|
282
|
-
).scalar()
|
283
|
-
if existing is not None:
|
284
|
-
return existing.id
|
285
|
-
contact = Contact(
|
286
|
-
jid=contact_jid.bare, legacy_id=legacy_id, user_account_id=user_pk
|
287
|
-
)
|
288
|
-
session.add(contact)
|
289
|
-
session.commit()
|
290
|
-
return contact.id
|
291
|
-
|
292
270
|
def get_all(self, user_pk: int) -> Iterator[Contact]:
|
293
271
|
with self.session() as session:
|
294
272
|
yield from session.execute(
|
@@ -375,20 +353,36 @@ class ContactStore(UpdatedMixin):
|
|
375
353
|
return None
|
376
354
|
return contact.avatar.legacy_id
|
377
355
|
|
378
|
-
def update(self, contact: "LegacyContact"):
|
356
|
+
def update(self, contact: "LegacyContact", commit=True) -> int:
|
379
357
|
with self.session() as session:
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
358
|
+
if contact.contact_pk is None:
|
359
|
+
if contact.cached_presence is not None:
|
360
|
+
presence_kwargs = contact.cached_presence._asdict()
|
361
|
+
presence_kwargs["cached_presence"] = True
|
362
|
+
else:
|
363
|
+
presence_kwargs = {}
|
364
|
+
row = Contact(
|
365
|
+
jid=contact.jid.bare,
|
366
|
+
legacy_id=str(contact.legacy_id),
|
367
|
+
user_account_id=contact.user_pk,
|
368
|
+
**presence_kwargs,
|
389
369
|
)
|
390
|
-
|
391
|
-
|
370
|
+
else:
|
371
|
+
row = (
|
372
|
+
session.query(Contact)
|
373
|
+
.filter(Contact.id == contact.contact_pk)
|
374
|
+
.one()
|
375
|
+
)
|
376
|
+
row.nick = contact.name
|
377
|
+
row.is_friend = contact.is_friend
|
378
|
+
row.added_to_roster = contact.added_to_roster
|
379
|
+
row.updated = True
|
380
|
+
row.extra_attributes = contact.serialize_extra_attributes()
|
381
|
+
row.caps_ver = contact._caps_ver
|
382
|
+
session.add(row)
|
383
|
+
if commit:
|
384
|
+
session.commit()
|
385
|
+
return row.id
|
392
386
|
|
393
387
|
def add_to_sent(self, contact_pk: int, msg_id: str) -> None:
|
394
388
|
with self.session() as session:
|
@@ -422,19 +416,52 @@ class ContactStore(UpdatedMixin):
|
|
422
416
|
)
|
423
417
|
session.commit()
|
424
418
|
|
419
|
+
def set_added_to_roster(self, contact_pk: int, value: bool) -> None:
|
420
|
+
with self.session() as session:
|
421
|
+
session.execute(
|
422
|
+
update(Contact)
|
423
|
+
.where(Contact.id == contact_pk)
|
424
|
+
.values(added_to_roster=value)
|
425
|
+
)
|
426
|
+
session.commit()
|
427
|
+
|
428
|
+
def delete(self, contact_pk: int) -> None:
|
429
|
+
with self.session() as session:
|
430
|
+
session.execute(delete(Contact).where(Contact.id == contact_pk))
|
431
|
+
session.commit()
|
432
|
+
|
425
433
|
|
426
434
|
class MAMStore(EngineMixin):
|
435
|
+
def __init__(self, *a, **kw):
|
436
|
+
super().__init__(*a, **kw)
|
437
|
+
with self.session() as session:
|
438
|
+
session.execute(
|
439
|
+
update(ArchivedMessage).values(source=ArchivedMessageSource.BACKFILL)
|
440
|
+
)
|
441
|
+
session.commit()
|
442
|
+
|
427
443
|
def nuke_older_than(self, days: int) -> None:
|
428
444
|
with self.session() as session:
|
429
445
|
session.execute(
|
430
446
|
delete(ArchivedMessage).where(
|
431
|
-
ArchivedMessage.timestamp
|
447
|
+
ArchivedMessage.timestamp < datetime.now() - timedelta(days=days)
|
432
448
|
)
|
433
449
|
)
|
434
450
|
session.commit()
|
435
451
|
|
436
|
-
def add_message(
|
452
|
+
def add_message(
|
453
|
+
self,
|
454
|
+
room_pk: int,
|
455
|
+
message: HistoryMessage,
|
456
|
+
archive_only: bool,
|
457
|
+
legacy_msg_id: str | None,
|
458
|
+
) -> None:
|
437
459
|
with self.session() as session:
|
460
|
+
source = (
|
461
|
+
ArchivedMessageSource.BACKFILL
|
462
|
+
if archive_only
|
463
|
+
else ArchivedMessageSource.LIVE
|
464
|
+
)
|
438
465
|
existing = session.execute(
|
439
466
|
select(ArchivedMessage)
|
440
467
|
.where(ArchivedMessage.room_id == room_pk)
|
@@ -445,6 +472,8 @@ class MAMStore(EngineMixin):
|
|
445
472
|
existing.timestamp = message.when
|
446
473
|
existing.stanza = str(message.stanza)
|
447
474
|
existing.author_jid = message.stanza.get_from()
|
475
|
+
existing.source = source
|
476
|
+
existing.legacy_id = legacy_msg_id
|
448
477
|
session.add(existing)
|
449
478
|
session.commit()
|
450
479
|
return
|
@@ -454,6 +483,8 @@ class MAMStore(EngineMixin):
|
|
454
483
|
stanza=str(message.stanza),
|
455
484
|
author_jid=message.stanza.get_from(),
|
456
485
|
room_id=room_pk,
|
486
|
+
source=source,
|
487
|
+
legacy_id=legacy_msg_id,
|
457
488
|
)
|
458
489
|
session.add(mam_msg)
|
459
490
|
session.commit()
|
@@ -526,25 +557,83 @@ class MAMStore(EngineMixin):
|
|
526
557
|
stanza=str(h.stanza), when=h.timestamp.replace(tzinfo=timezone.utc)
|
527
558
|
)
|
528
559
|
|
529
|
-
def
|
530
|
-
r = []
|
560
|
+
def get_first(self, room_pk: int, with_legacy_id=False) -> ArchivedMessage | None:
|
531
561
|
with self.session() as session:
|
532
|
-
|
533
|
-
select(ArchivedMessage
|
562
|
+
q = (
|
563
|
+
select(ArchivedMessage)
|
534
564
|
.where(ArchivedMessage.room_id == room_pk)
|
535
565
|
.order_by(ArchivedMessage.timestamp.asc())
|
536
|
-
)
|
566
|
+
)
|
567
|
+
if with_legacy_id:
|
568
|
+
q = q.filter(ArchivedMessage.legacy_id.isnot(None))
|
569
|
+
return session.execute(q).scalar()
|
570
|
+
|
571
|
+
def get_last(
|
572
|
+
self, room_pk: int, source: ArchivedMessageSource | None = None
|
573
|
+
) -> ArchivedMessage | None:
|
574
|
+
with self.session() as session:
|
575
|
+
q = select(ArchivedMessage).where(ArchivedMessage.room_id == room_pk)
|
576
|
+
|
577
|
+
if source is not None:
|
578
|
+
q = q.where(ArchivedMessage.source == source)
|
579
|
+
|
580
|
+
return session.execute(
|
581
|
+
q.order_by(ArchivedMessage.timestamp.desc())
|
582
|
+
).scalar()
|
583
|
+
|
584
|
+
def get_first_and_last(self, room_pk: int) -> list[MamMetadata]:
|
585
|
+
r = []
|
586
|
+
with self.session():
|
587
|
+
first = self.get_first(room_pk)
|
537
588
|
if first is not None:
|
538
|
-
r.append(MamMetadata(
|
539
|
-
last =
|
540
|
-
select(ArchivedMessage.stanza_id, ArchivedMessage.timestamp)
|
541
|
-
.where(ArchivedMessage.room_id == room_pk)
|
542
|
-
.order_by(ArchivedMessage.timestamp.desc())
|
543
|
-
).first()
|
589
|
+
r.append(MamMetadata(first.stanza_id, first.timestamp))
|
590
|
+
last = self.get_last(room_pk)
|
544
591
|
if last is not None:
|
545
|
-
r.append(MamMetadata(
|
592
|
+
r.append(MamMetadata(last.stanza_id, last.timestamp))
|
546
593
|
return r
|
547
594
|
|
595
|
+
def get_most_recent_with_legacy_id(
|
596
|
+
self, room_pk: int, source: ArchivedMessageSource | None = None
|
597
|
+
) -> ArchivedMessage | None:
|
598
|
+
with self.session() as session:
|
599
|
+
q = (
|
600
|
+
select(ArchivedMessage)
|
601
|
+
.where(ArchivedMessage.room_id == room_pk)
|
602
|
+
.where(ArchivedMessage.legacy_id.isnot(None))
|
603
|
+
)
|
604
|
+
if source is not None:
|
605
|
+
q = q.where(ArchivedMessage.source == source)
|
606
|
+
return session.execute(
|
607
|
+
q.order_by(ArchivedMessage.timestamp.desc())
|
608
|
+
).scalar()
|
609
|
+
|
610
|
+
def get_least_recent_with_legacy_id_after(
|
611
|
+
self, room_pk: int, after_id: str, source=ArchivedMessageSource.LIVE
|
612
|
+
) -> ArchivedMessage | None:
|
613
|
+
with self.session() as session:
|
614
|
+
after_timestamp = (
|
615
|
+
session.query(ArchivedMessage.timestamp)
|
616
|
+
.filter(ArchivedMessage.legacy_id == after_id)
|
617
|
+
.scalar()
|
618
|
+
)
|
619
|
+
q = (
|
620
|
+
select(ArchivedMessage)
|
621
|
+
.where(ArchivedMessage.room_id == room_pk)
|
622
|
+
.where(ArchivedMessage.legacy_id.isnot(None))
|
623
|
+
.where(ArchivedMessage.source == source)
|
624
|
+
.where(ArchivedMessage.timestamp > after_timestamp)
|
625
|
+
)
|
626
|
+
return session.execute(q.order_by(ArchivedMessage.timestamp.asc())).scalar()
|
627
|
+
|
628
|
+
def get_by_legacy_id(self, room_pk: int, legacy_id: str) -> ArchivedMessage | None:
|
629
|
+
with self.session() as session:
|
630
|
+
return (
|
631
|
+
session.query(ArchivedMessage)
|
632
|
+
.filter(ArchivedMessage.room_id == room_pk)
|
633
|
+
.filter(ArchivedMessage.legacy_id == legacy_id)
|
634
|
+
.first()
|
635
|
+
)
|
636
|
+
|
548
637
|
|
549
638
|
class MultiStore(EngineMixin):
|
550
639
|
def get_xmpp_ids(self, user_pk: int, xmpp_id: str) -> list[str]:
|
@@ -676,27 +765,13 @@ class RoomStore(UpdatedMixin):
|
|
676
765
|
super().__init__(*a, **kw)
|
677
766
|
with self.session() as session:
|
678
767
|
session.execute(
|
679
|
-
update(Room).values(
|
768
|
+
update(Room).values(
|
769
|
+
subject_setter=None, user_resources=None, history_filled=False
|
770
|
+
)
|
680
771
|
)
|
681
772
|
session.commit()
|
682
773
|
|
683
|
-
def
|
684
|
-
if jid.resource:
|
685
|
-
raise TypeError
|
686
|
-
with self.session() as session:
|
687
|
-
existing = session.execute(
|
688
|
-
select(Room.id)
|
689
|
-
.where(Room.user_account_id == user_pk)
|
690
|
-
.where(Room.legacy_id == legacy_id)
|
691
|
-
).scalar()
|
692
|
-
if existing is not None:
|
693
|
-
return existing
|
694
|
-
room = Room(jid=jid, user_account_id=user_pk, legacy_id=legacy_id)
|
695
|
-
session.add(room)
|
696
|
-
session.commit()
|
697
|
-
return room.id
|
698
|
-
|
699
|
-
def set_avatar(self, room_pk: int, avatar_pk: int) -> None:
|
774
|
+
def set_avatar(self, room_pk: int, avatar_pk: int | None) -> None:
|
700
775
|
with self.session() as session:
|
701
776
|
session.execute(
|
702
777
|
update(Room).where(Room.id == room_pk).values(avatar_id=avatar_pk)
|
@@ -728,43 +803,45 @@ class RoomStore(UpdatedMixin):
|
|
728
803
|
.where(Room.legacy_id == legacy_id)
|
729
804
|
).scalar()
|
730
805
|
|
731
|
-
def
|
732
|
-
from slidge.contact import LegacyContact
|
733
|
-
|
806
|
+
def update_subject_setter(self, room_pk: int, subject_setter: str | None):
|
734
807
|
with self.session() as session:
|
735
|
-
if room.subject_setter is None:
|
736
|
-
subject_setter_id = None
|
737
|
-
elif isinstance(room.subject_setter, str):
|
738
|
-
subject_setter_id = None
|
739
|
-
elif isinstance(room.subject_setter, LegacyContact):
|
740
|
-
subject_setter_id = None
|
741
|
-
elif room.subject_setter.is_system:
|
742
|
-
subject_setter_id = None
|
743
|
-
else:
|
744
|
-
subject_setter_id = room.subject_setter.pk
|
745
|
-
|
746
808
|
session.execute(
|
747
809
|
update(Room)
|
748
|
-
.where(Room.id ==
|
749
|
-
.values(
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
),
|
759
|
-
|
760
|
-
subject=room.subject,
|
761
|
-
subject_date=room.subject_date,
|
762
|
-
subject_setter_id=subject_setter_id,
|
763
|
-
participants_filled=room._participants_filled,
|
764
|
-
n_participants=room._n_participants,
|
810
|
+
.where(Room.id == room_pk)
|
811
|
+
.values(subject_setter=subject_setter)
|
812
|
+
)
|
813
|
+
session.commit()
|
814
|
+
|
815
|
+
def update(self, muc: "LegacyMUC") -> int:
|
816
|
+
with self.session() as session:
|
817
|
+
if muc.pk is None:
|
818
|
+
row = Room(
|
819
|
+
jid=muc.jid,
|
820
|
+
legacy_id=str(muc.legacy_id),
|
821
|
+
user_account_id=muc.user_pk,
|
765
822
|
)
|
823
|
+
else:
|
824
|
+
row = session.query(Room).filter(Room.id == muc.pk).one()
|
825
|
+
|
826
|
+
row.updated = True
|
827
|
+
row.extra_attributes = muc.serialize_extra_attributes()
|
828
|
+
row.name = muc.name
|
829
|
+
row.description = muc.description
|
830
|
+
row.user_resources = (
|
831
|
+
None
|
832
|
+
if not muc._user_resources
|
833
|
+
else json.dumps(list(muc._user_resources))
|
766
834
|
)
|
835
|
+
row.muc_type = muc.type
|
836
|
+
row.subject = muc.subject
|
837
|
+
row.subject_date = muc.subject_date
|
838
|
+
row.subject_setter = muc.subject_setter
|
839
|
+
row.participants_filled = muc._participants_filled
|
840
|
+
row.n_participants = muc._n_participants
|
841
|
+
row.user_nick = muc.user_nick
|
842
|
+
session.add(row)
|
767
843
|
session.commit()
|
844
|
+
return row.id
|
768
845
|
|
769
846
|
def update_subject_date(
|
770
847
|
self, room_pk: int, subject_date: Optional[datetime]
|
@@ -789,6 +866,11 @@ class RoomStore(UpdatedMixin):
|
|
789
866
|
)
|
790
867
|
session.commit()
|
791
868
|
|
869
|
+
def update_name(self, room_pk: int, name: Optional[str]) -> None:
|
870
|
+
with self.session() as session:
|
871
|
+
session.execute(update(Room).where(Room.id == room_pk).values(name=name))
|
872
|
+
session.commit()
|
873
|
+
|
792
874
|
def update_n_participants(self, room_pk: int, n: Optional[int]) -> None:
|
793
875
|
with self.session() as session:
|
794
876
|
session.execute(
|
@@ -826,6 +908,20 @@ class RoomStore(UpdatedMixin):
|
|
826
908
|
is None
|
827
909
|
)
|
828
910
|
|
911
|
+
def set_participants_filled(self, room_pk: int, val=True) -> None:
|
912
|
+
with self.session() as session:
|
913
|
+
session.execute(
|
914
|
+
update(Room).where(Room.id == room_pk).values(participants_filled=val)
|
915
|
+
)
|
916
|
+
session.commit()
|
917
|
+
|
918
|
+
def set_history_filled(self, room_pk: int, val=True) -> None:
|
919
|
+
with self.session() as session:
|
920
|
+
session.execute(
|
921
|
+
update(Room).where(Room.id == room_pk).values(history_filled=True)
|
922
|
+
)
|
923
|
+
session.commit()
|
924
|
+
|
829
925
|
def get_all(self, user_pk: int) -> Iterator[Room]:
|
830
926
|
with self.session() as session:
|
831
927
|
yield from session.execute(
|
@@ -971,6 +1067,12 @@ class ParticipantStore(EngineMixin):
|
|
971
1067
|
with self.session() as session:
|
972
1068
|
session.execute(delete(Participant).where(Participant.id == participant_pk))
|
973
1069
|
|
1070
|
+
def get_count(self, room_pk: int) -> int:
|
1071
|
+
with self.session() as session:
|
1072
|
+
return session.query(
|
1073
|
+
count(Participant.id).filter(Participant.room_id == room_pk)
|
1074
|
+
).scalar()
|
1075
|
+
|
974
1076
|
|
975
1077
|
log = logging.getLogger(__name__)
|
976
1078
|
_session: Optional[Session] = None
|
slidge/group/archive.py
CHANGED
@@ -6,8 +6,10 @@ from typing import TYPE_CHECKING, Collection, Optional
|
|
6
6
|
|
7
7
|
from slixmpp import Iq, Message
|
8
8
|
|
9
|
+
from ..db.models import ArchivedMessage, ArchivedMessageSource
|
9
10
|
from ..db.store import MAMStore
|
10
11
|
from ..util.archive_msg import HistoryMessage
|
12
|
+
from ..util.types import HoleBound
|
11
13
|
|
12
14
|
if TYPE_CHECKING:
|
13
15
|
from .participant import LegacyParticipant
|
@@ -22,12 +24,16 @@ class MessageArchive:
|
|
22
24
|
self,
|
23
25
|
msg: Message,
|
24
26
|
participant: Optional["LegacyParticipant"] = None,
|
27
|
+
archive_only=False,
|
28
|
+
legacy_msg_id=None,
|
25
29
|
):
|
26
30
|
"""
|
27
31
|
Add a message to the archive if it is deemed archivable
|
28
32
|
|
29
33
|
:param msg:
|
30
34
|
:param participant:
|
35
|
+
:param archive_only:
|
36
|
+
:param legacy_msg_id:
|
31
37
|
"""
|
32
38
|
if not archivable(msg):
|
33
39
|
return
|
@@ -47,11 +53,50 @@ class MessageArchive:
|
|
47
53
|
"jid"
|
48
54
|
] = f"{uuid.uuid4()}@{participant.xmpp.boundjid.bare}"
|
49
55
|
|
50
|
-
self.__store.add_message(
|
56
|
+
self.__store.add_message(
|
57
|
+
self.room_pk,
|
58
|
+
HistoryMessage(new_msg),
|
59
|
+
archive_only,
|
60
|
+
None if legacy_msg_id is None else str(legacy_msg_id),
|
61
|
+
)
|
51
62
|
|
52
63
|
def __iter__(self):
|
53
64
|
return iter(self.get_all())
|
54
65
|
|
66
|
+
@staticmethod
|
67
|
+
def __to_bound(stored: ArchivedMessage):
|
68
|
+
return HoleBound(
|
69
|
+
stored.legacy_id, # type:ignore
|
70
|
+
stored.timestamp.replace(tzinfo=timezone.utc),
|
71
|
+
)
|
72
|
+
|
73
|
+
def get_hole_bounds(self) -> tuple[HoleBound | None, HoleBound | None]:
|
74
|
+
most_recent = self.__store.get_most_recent_with_legacy_id(self.room_pk)
|
75
|
+
if most_recent is None:
|
76
|
+
return None, None
|
77
|
+
if most_recent.source == ArchivedMessageSource.BACKFILL:
|
78
|
+
# most recent = only backfill, fetch everything since last backfill
|
79
|
+
return self.__to_bound(most_recent), None
|
80
|
+
|
81
|
+
most_recent_back_filled = self.__store.get_most_recent_with_legacy_id(
|
82
|
+
self.room_pk, ArchivedMessageSource.BACKFILL
|
83
|
+
)
|
84
|
+
if most_recent_back_filled is None:
|
85
|
+
# group was never back-filled, fetch everything before first live
|
86
|
+
least_recent_live = self.__store.get_first(self.room_pk, True)
|
87
|
+
assert least_recent_live is not None
|
88
|
+
return None, self.__to_bound(least_recent_live)
|
89
|
+
|
90
|
+
assert most_recent_back_filled.legacy_id is not None
|
91
|
+
least_recent_live = self.__store.get_least_recent_with_legacy_id_after(
|
92
|
+
self.room_pk, most_recent_back_filled.legacy_id
|
93
|
+
)
|
94
|
+
assert least_recent_live is not None
|
95
|
+
# this is a hole caused by slidge downtime
|
96
|
+
return self.__to_bound(most_recent_back_filled), self.__to_bound(
|
97
|
+
least_recent_live
|
98
|
+
)
|
99
|
+
|
55
100
|
def get_all(
|
56
101
|
self,
|
57
102
|
start_date: Optional[datetime] = None,
|
slidge/group/bookmarks.py
CHANGED
@@ -3,6 +3,7 @@ import logging
|
|
3
3
|
from typing import TYPE_CHECKING, Generic, Iterator, Optional, Type
|
4
4
|
|
5
5
|
from slixmpp import JID
|
6
|
+
from slixmpp.exceptions import XMPPError
|
6
7
|
from slixmpp.jid import _unescape_node
|
7
8
|
|
8
9
|
from ..contact.roster import ESCAPE_TABLE
|
@@ -57,15 +58,26 @@ class LegacyBookmarks(
|
|
57
58
|
return f"<Bookmarks of {self.user_jid}>"
|
58
59
|
|
59
60
|
async def __finish_init_muc(self, legacy_id: LegacyGroupIdType, jid: JID):
|
60
|
-
muc = self._muc_class(self.session, legacy_id=legacy_id, jid=jid)
|
61
61
|
with self.__store.session():
|
62
|
-
|
63
|
-
|
64
|
-
|
62
|
+
stored = self.__store.get_by_legacy_id(self.session.user_pk, str(legacy_id))
|
63
|
+
if stored is not None:
|
64
|
+
if stored.updated:
|
65
|
+
return self._muc_class.from_store(self.session, stored)
|
66
|
+
muc = self._muc_class(self.session, legacy_id=legacy_id, jid=jid)
|
67
|
+
muc.pk = stored.id
|
68
|
+
else:
|
69
|
+
muc = self._muc_class(self.session, legacy_id=legacy_id, jid=jid)
|
70
|
+
|
71
|
+
try:
|
72
|
+
with muc.updating_info():
|
73
|
+
await muc.avatar_wrap_update_info()
|
74
|
+
except Exception as e:
|
75
|
+
raise XMPPError("internal-server-error", str(e))
|
65
76
|
if not muc.user_nick:
|
66
77
|
muc.user_nick = self._user_nick
|
67
78
|
self.log.debug("MUC created: %r", muc)
|
68
|
-
self.__store.update(muc)
|
79
|
+
muc.pk = self.__store.update(muc)
|
80
|
+
muc.archive = MessageArchive(muc.pk, self.xmpp.store.mam)
|
69
81
|
return muc
|
70
82
|
|
71
83
|
async def legacy_id_to_jid_local_part(self, legacy_id: LegacyGroupIdType):
|
slidge/group/participant.py
CHANGED
@@ -117,6 +117,8 @@ class LegacyParticipant(
|
|
117
117
|
if self._affiliation == affiliation:
|
118
118
|
return
|
119
119
|
self._affiliation = affiliation
|
120
|
+
if not self.muc._participants_filled:
|
121
|
+
return
|
120
122
|
self.__part_store.set_affiliation(self.pk, affiliation)
|
121
123
|
if not self._presence_sent:
|
122
124
|
return
|
@@ -145,6 +147,8 @@ class LegacyParticipant(
|
|
145
147
|
if self._role == role:
|
146
148
|
return
|
147
149
|
self._role = role
|
150
|
+
if not self.muc._participants_filled:
|
151
|
+
return
|
148
152
|
self.__part_store.set_role(self.pk, role)
|
149
153
|
if not self._presence_sent:
|
150
154
|
return
|
@@ -154,6 +158,8 @@ class LegacyParticipant(
|
|
154
158
|
if self._hats == hats:
|
155
159
|
return
|
156
160
|
self._hats = hats
|
161
|
+
if not self.muc._participants_filled:
|
162
|
+
return
|
157
163
|
self.__part_store.set_hats(self.pk, hats)
|
158
164
|
if not self._presence_sent:
|
159
165
|
return
|
@@ -313,6 +319,7 @@ class LegacyParticipant(
|
|
313
319
|
stanza: MessageOrPresenceTypeVar,
|
314
320
|
full_jid: Optional[JID] = None,
|
315
321
|
archive_only=False,
|
322
|
+
legacy_msg_id=None,
|
316
323
|
**send_kwargs,
|
317
324
|
) -> MessageOrPresenceTypeVar:
|
318
325
|
stanza["occupant-id"]["id"] = self.__occupant_id
|
@@ -331,8 +338,8 @@ class LegacyParticipant(
|
|
331
338
|
else:
|
332
339
|
stanza.send()
|
333
340
|
else:
|
334
|
-
if isinstance(stanza, Message):
|
335
|
-
self.muc.archive.add(stanza, self)
|
341
|
+
if hasattr(self.muc, "archive") and isinstance(stanza, Message):
|
342
|
+
self.muc.archive.add(stanza, self, archive_only, legacy_msg_id)
|
336
343
|
if archive_only:
|
337
344
|
return stanza
|
338
345
|
for user_full_jid in self.muc.user_full_jids():
|
@@ -460,7 +467,7 @@ class LegacyParticipant(
|
|
460
467
|
):
|
461
468
|
if update_muc:
|
462
469
|
self.muc._subject = subject # type: ignore
|
463
|
-
self.muc.subject_setter = self
|
470
|
+
self.muc.subject_setter = self.nickname
|
464
471
|
self.muc.subject_date = when
|
465
472
|
|
466
473
|
msg = self._make_message()
|