slidge 0.2.0a0__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/__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/group/room.py
CHANGED
@@ -5,7 +5,7 @@ import string
|
|
5
5
|
import warnings
|
6
6
|
from copy import copy
|
7
7
|
from datetime import datetime, timedelta, timezone
|
8
|
-
from typing import TYPE_CHECKING, Generic, Optional, Self, Union
|
8
|
+
from typing import TYPE_CHECKING, AsyncIterator, Generic, Optional, Self, Union
|
9
9
|
from uuid import uuid4
|
10
10
|
|
11
11
|
from slixmpp import JID, Iq, Message, Presence
|
@@ -21,12 +21,14 @@ from ..contact.roster import ContactIsUser
|
|
21
21
|
from ..core import config
|
22
22
|
from ..core.mixins import StoredAttributeMixin
|
23
23
|
from ..core.mixins.avatar import AvatarMixin
|
24
|
+
from ..core.mixins.db import UpdateInfoMixin
|
24
25
|
from ..core.mixins.disco import ChatterDiscoMixin
|
25
26
|
from ..core.mixins.lock import NamedLockMixin
|
26
27
|
from ..core.mixins.recipient import ReactionRecipientMixin, ThreadRecipientMixin
|
27
28
|
from ..db.models import Room
|
28
29
|
from ..util import ABCSubclassableOnceAtMost
|
29
30
|
from ..util.types import (
|
31
|
+
HoleBound,
|
30
32
|
LegacyGroupIdType,
|
31
33
|
LegacyMessageType,
|
32
34
|
LegacyParticipantType,
|
@@ -35,7 +37,7 @@ from ..util.types import (
|
|
35
37
|
MucAffiliation,
|
36
38
|
MucType,
|
37
39
|
)
|
38
|
-
from ..util.util import deprecated
|
40
|
+
from ..util.util import deprecated, timeit, with_session
|
39
41
|
from .archive import MessageArchive
|
40
42
|
from .participant import LegacyParticipant
|
41
43
|
|
@@ -45,11 +47,14 @@ if TYPE_CHECKING:
|
|
45
47
|
|
46
48
|
ADMIN_NS = "http://jabber.org/protocol/muc#admin"
|
47
49
|
|
50
|
+
SubjectSetterType = Union[str, None, "LegacyContact", "LegacyParticipant"]
|
51
|
+
|
48
52
|
|
49
53
|
class LegacyMUC(
|
50
54
|
Generic[
|
51
55
|
LegacyGroupIdType, LegacyMessageType, LegacyParticipantType, LegacyUserIdType
|
52
56
|
],
|
57
|
+
UpdateInfoMixin,
|
53
58
|
StoredAttributeMixin,
|
54
59
|
AvatarMixin,
|
55
60
|
NamedLockMixin,
|
@@ -120,7 +125,6 @@ class LegacyMUC(
|
|
120
125
|
tries to set the room subject.
|
121
126
|
"""
|
122
127
|
|
123
|
-
_avatar_pubsub_broadcast = False
|
124
128
|
_avatar_bare_jid = True
|
125
129
|
archive: MessageArchive
|
126
130
|
|
@@ -137,15 +141,13 @@ class LegacyMUC(
|
|
137
141
|
self.Participant = LegacyParticipant.get_self_or_unique_subclass()
|
138
142
|
|
139
143
|
self._subject = ""
|
140
|
-
self._subject_setter:
|
141
|
-
None
|
142
|
-
)
|
144
|
+
self._subject_setter: Optional[str] = None
|
143
145
|
|
144
146
|
self.pk: Optional[int] = None
|
145
147
|
self._user_nick: Optional[str] = None
|
146
148
|
|
147
149
|
self._participants_filled = False
|
148
|
-
self.
|
150
|
+
self._history_filled = False
|
149
151
|
self._description = ""
|
150
152
|
self._subject_date: Optional[datetime] = None
|
151
153
|
|
@@ -165,6 +167,8 @@ class LegacyMUC(
|
|
165
167
|
if self._n_participants == n_participants:
|
166
168
|
return
|
167
169
|
self._n_participants = n_participants
|
170
|
+
if self._updating_info:
|
171
|
+
return
|
168
172
|
assert self.pk is not None
|
169
173
|
self.__store.update_n_participants(self.pk, n_participants)
|
170
174
|
|
@@ -182,6 +186,8 @@ class LegacyMUC(
|
|
182
186
|
@subject_date.setter
|
183
187
|
def subject_date(self, when: Optional[datetime]) -> None:
|
184
188
|
self._subject_date = when
|
189
|
+
if self._updating_info:
|
190
|
+
return
|
185
191
|
assert self.pk is not None
|
186
192
|
self.__store.update_subject_date(self.pk, when)
|
187
193
|
|
@@ -211,39 +217,66 @@ class LegacyMUC(
|
|
211
217
|
self.__store.set_resource(self.pk, self._user_resources)
|
212
218
|
|
213
219
|
async def __fill_participants(self):
|
220
|
+
if self._participants_filled:
|
221
|
+
return
|
222
|
+
assert self.pk is not None
|
214
223
|
async with self.lock("fill participants"):
|
215
|
-
if self._participants_filled:
|
216
|
-
return
|
217
224
|
self._participants_filled = True
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
225
|
+
async for p in self.fill_participants():
|
226
|
+
self.__participants_store.update(p)
|
227
|
+
self.__store.set_participants_filled(self.pk)
|
228
|
+
|
229
|
+
async def get_participants(self) -> AsyncIterator[LegacyParticipant]:
|
230
|
+
assert self.pk is not None
|
231
|
+
if self._participants_filled:
|
232
|
+
for db_participant in self.xmpp.store.participants.get_all(
|
233
|
+
self.pk, user_included=True
|
234
|
+
):
|
235
|
+
participant = self.Participant.from_store(self.session, db_participant)
|
236
|
+
yield participant
|
237
|
+
return
|
238
|
+
|
239
|
+
async with self.lock("fill participants"):
|
240
|
+
self._participants_filled = True
|
241
|
+
# We only fill the participants list if/when the MUC is first
|
242
|
+
# joined by an XMPP client. But we may have instantiated
|
243
|
+
resources = set[str]()
|
244
|
+
for db_participant in self.xmpp.store.participants.get_all(
|
245
|
+
self.pk, user_included=True
|
246
|
+
):
|
247
|
+
participant = self.Participant.from_store(self.session, db_participant)
|
248
|
+
resources.add(participant.jid.resource)
|
249
|
+
yield participant
|
250
|
+
async for p in self.fill_participants():
|
251
|
+
if p.jid.resource not in resources:
|
252
|
+
yield p
|
253
|
+
self.__store.set_participants_filled(self.pk)
|
254
|
+
return
|
222
255
|
|
223
256
|
async def __fill_history(self):
|
224
257
|
async with self.lock("fill history"):
|
225
|
-
if self.
|
258
|
+
if self._history_filled:
|
226
259
|
log.debug("History has already been fetched %s", self)
|
227
260
|
return
|
228
261
|
log.debug("Fetching history for %s", self)
|
229
|
-
for msg in self.archive:
|
230
|
-
try:
|
231
|
-
legacy_id = self.session.xmpp_to_legacy_msg_id(msg.id)
|
232
|
-
oldest_date = msg.when
|
233
|
-
except Exception as e:
|
234
|
-
# not all archived stanzas have a valid legacy msg ID, eg
|
235
|
-
# reactions, corrections, message with multiple attachments…
|
236
|
-
self.log.debug(f"Could not convert during history back-filling {e}")
|
237
|
-
else:
|
238
|
-
break
|
239
|
-
else:
|
240
|
-
legacy_id = None
|
241
|
-
oldest_date = None
|
242
262
|
try:
|
243
|
-
|
263
|
+
before, after = self.archive.get_hole_bounds()
|
264
|
+
if before is not None:
|
265
|
+
before = before._replace(
|
266
|
+
id=self.xmpp.LEGACY_MSG_ID_TYPE(before.id) # type:ignore
|
267
|
+
)
|
268
|
+
if after is not None:
|
269
|
+
after = after._replace(
|
270
|
+
id=self.xmpp.LEGACY_MSG_ID_TYPE(after.id) # type:ignore
|
271
|
+
)
|
272
|
+
await self.backfill(before, after)
|
244
273
|
except NotImplementedError:
|
245
274
|
return
|
246
|
-
|
275
|
+
except Exception as e:
|
276
|
+
log.exception("Could not backfill: %s", e)
|
277
|
+
assert self.pk is not None
|
278
|
+
self.__store.set_history_filled(self.pk, True)
|
279
|
+
self._history_filled = True
|
247
280
|
|
248
281
|
@property
|
249
282
|
def name(self):
|
@@ -255,6 +288,10 @@ class LegacyMUC(
|
|
255
288
|
return
|
256
289
|
self.DISCO_NAME = n
|
257
290
|
self.__send_configuration_change((104,))
|
291
|
+
if self._updating_info:
|
292
|
+
return
|
293
|
+
assert self.pk is not None
|
294
|
+
self.__store.update_name(self.pk, n)
|
258
295
|
|
259
296
|
@property
|
260
297
|
def description(self):
|
@@ -265,9 +302,11 @@ class LegacyMUC(
|
|
265
302
|
if self._description == d:
|
266
303
|
return
|
267
304
|
self._description = d
|
305
|
+
self.__send_configuration_change((104,))
|
306
|
+
if self._updating_info:
|
307
|
+
return
|
268
308
|
assert self.pk is not None
|
269
309
|
self.__store.update_description(self.pk, d)
|
270
|
-
self.__send_configuration_change((104,))
|
271
310
|
|
272
311
|
def on_presence_unavailable(self, p: Presence):
|
273
312
|
pto = p.get_to()
|
@@ -307,27 +346,35 @@ class LegacyMUC(
|
|
307
346
|
|
308
347
|
async def backfill(
|
309
348
|
self,
|
310
|
-
|
311
|
-
|
349
|
+
after: Optional[HoleBound] = None,
|
350
|
+
before: Optional[HoleBound] = None,
|
312
351
|
):
|
313
352
|
"""
|
314
|
-
Override this if the legacy network provide server-side
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
:param
|
321
|
-
|
353
|
+
Override this if the legacy network provide server-side group archives.
|
354
|
+
|
355
|
+
In it, send history messages using ``self.get_participant(xxx).send_xxxx``,
|
356
|
+
with the ``archive_only=True`` kwarg. This is only called once per slidge
|
357
|
+
run for a given group.
|
358
|
+
|
359
|
+
:param after: Fetch messages after this one. If ``None``, it's up to you
|
360
|
+
to decide how far you want to go in the archive. If it's not ``None``,
|
361
|
+
it means slidge has some messages in this archive and you should really try
|
362
|
+
to complete it to avoid "holes" in the history of this group.
|
363
|
+
:param before: Fetch messages before this one. If ``None``, fetch all messages
|
364
|
+
up to the most recent one
|
322
365
|
"""
|
323
366
|
raise NotImplementedError
|
324
367
|
|
325
|
-
async def fill_participants(self):
|
368
|
+
async def fill_participants(self) -> AsyncIterator[LegacyParticipant]:
|
326
369
|
"""
|
327
|
-
|
328
|
-
|
370
|
+
This method should yield the list of all members of this group.
|
371
|
+
|
372
|
+
Typically, use ``participant = self.get_participant()``, self.get_participant_by_contact(),
|
373
|
+
of self.get_user_participant(), and update their affiliation, hats, etc.
|
374
|
+
before yielding them.
|
329
375
|
"""
|
330
|
-
|
376
|
+
return
|
377
|
+
yield
|
331
378
|
|
332
379
|
@property
|
333
380
|
def subject(self):
|
@@ -337,14 +384,13 @@ class LegacyMUC(
|
|
337
384
|
def subject(self, s: str):
|
338
385
|
if s == self._subject:
|
339
386
|
return
|
340
|
-
self.
|
341
|
-
self.
|
342
|
-
).add_done_callback(
|
343
|
-
lambda task: task.result().set_room_subject(
|
344
|
-
s, None, self.subject_date, False
|
345
|
-
)
|
387
|
+
self.__get_subject_setter_participant().set_room_subject(
|
388
|
+
s, None, self.subject_date, False
|
346
389
|
)
|
390
|
+
|
347
391
|
self._subject = s
|
392
|
+
if self._updating_info:
|
393
|
+
return
|
348
394
|
assert self.pk is not None
|
349
395
|
self.__store.update_subject(self.pk, s)
|
350
396
|
|
@@ -353,25 +399,29 @@ class LegacyMUC(
|
|
353
399
|
return self.type == MucType.CHANNEL
|
354
400
|
|
355
401
|
@property
|
356
|
-
def subject_setter(self):
|
402
|
+
def subject_setter(self) -> Optional[str]:
|
357
403
|
return self._subject_setter
|
358
404
|
|
359
405
|
@subject_setter.setter
|
360
|
-
def subject_setter(self, subject_setter):
|
361
|
-
|
362
|
-
|
406
|
+
def subject_setter(self, subject_setter: SubjectSetterType) -> None:
|
407
|
+
if isinstance(subject_setter, LegacyContact):
|
408
|
+
subject_setter = subject_setter.name
|
409
|
+
elif isinstance(subject_setter, LegacyParticipant):
|
410
|
+
subject_setter = subject_setter.nickname
|
363
411
|
|
364
|
-
|
365
|
-
|
412
|
+
if subject_setter == self._subject_setter:
|
413
|
+
return
|
414
|
+
assert isinstance(subject_setter, str)
|
415
|
+
self._subject_setter = subject_setter
|
416
|
+
if self._updating_info:
|
417
|
+
return
|
418
|
+
assert self.pk is not None
|
419
|
+
self.__store.update_subject_setter(self.pk, subject_setter)
|
366
420
|
|
367
|
-
|
368
|
-
|
369
|
-
elif isinstance(who, str):
|
370
|
-
return await self.get_participant(who, store=False)
|
371
|
-
elif isinstance(self.subject_setter, LegacyContact):
|
372
|
-
return await self.get_participant_by_contact(who)
|
373
|
-
else:
|
421
|
+
def __get_subject_setter_participant(self) -> LegacyParticipant:
|
422
|
+
if self._subject_setter is None:
|
374
423
|
return self.get_system_participant()
|
424
|
+
return self.Participant(self, self._subject_setter)
|
375
425
|
|
376
426
|
def features(self):
|
377
427
|
features = [
|
@@ -410,7 +460,8 @@ class LegacyMUC(
|
|
410
460
|
form.add_field("muc#roominfo_subjectmod", "boolean", value=False)
|
411
461
|
|
412
462
|
if self._ALL_INFO_FILLED_ON_STARTUP or self._participants_filled:
|
413
|
-
|
463
|
+
assert self.pk is not None
|
464
|
+
n: Optional[int] = self.__participants_store.get_count(self.pk)
|
414
465
|
else:
|
415
466
|
n = self._n_participants
|
416
467
|
if n is not None:
|
@@ -504,12 +555,14 @@ class LegacyMUC(
|
|
504
555
|
msg.send()
|
505
556
|
|
506
557
|
def _get_cached_avatar_id(self):
|
507
|
-
|
558
|
+
if self.pk is None:
|
559
|
+
return None
|
508
560
|
return self.xmpp.store.rooms.get_avatar_legacy_id(self.pk)
|
509
561
|
|
510
562
|
def _post_avatar_update(self) -> None:
|
511
|
-
if self.pk is None
|
563
|
+
if self.pk is None:
|
512
564
|
return
|
565
|
+
assert self.pk is not None
|
513
566
|
self.xmpp.store.rooms.set_avatar(self.pk, self._avatar_pk)
|
514
567
|
self.__send_configuration_change((104,))
|
515
568
|
self._send_room_presence()
|
@@ -527,6 +580,8 @@ class LegacyMUC(
|
|
527
580
|
p["vcard_temp_update"]["photo"] = ""
|
528
581
|
p.send()
|
529
582
|
|
583
|
+
@timeit
|
584
|
+
@with_session
|
530
585
|
async def join(self, join_presence: Presence):
|
531
586
|
user_full_jid = join_presence.get_from()
|
532
587
|
requested_nickname = join_presence.get_to().resource
|
@@ -548,17 +603,16 @@ class LegacyMUC(
|
|
548
603
|
requested_nickname,
|
549
604
|
)
|
550
605
|
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
participant = self.Participant.from_store(self.session, db_participant)
|
606
|
+
user_nick = self.user_nick
|
607
|
+
user_participant = None
|
608
|
+
async for participant in self.get_participants():
|
609
|
+
if participant.is_user:
|
610
|
+
user_participant = participant
|
611
|
+
continue
|
558
612
|
participant.send_initial_presence(full_jid=user_full_jid)
|
559
613
|
|
560
|
-
|
561
|
-
|
614
|
+
if user_participant is None:
|
615
|
+
user_participant = await self.get_user_participant()
|
562
616
|
if not user_participant.is_user: # type:ignore
|
563
617
|
self.log.warning("is_user flag not set participant on user_participant")
|
564
618
|
user_participant.is_user = True # type:ignore
|
@@ -589,7 +643,7 @@ class LegacyMUC(
|
|
589
643
|
maxstanzas=maxstanzas,
|
590
644
|
since=since,
|
591
645
|
)
|
592
|
-
|
646
|
+
self.__get_subject_setter_participant().set_room_subject(
|
593
647
|
self._subject if self.HAS_SUBJECT else (self.description or self.name),
|
594
648
|
user_full_jid,
|
595
649
|
self.subject_date,
|
@@ -643,20 +697,21 @@ class LegacyMUC(
|
|
643
697
|
construction (optional)
|
644
698
|
:return:
|
645
699
|
"""
|
646
|
-
if fill_first:
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
self.
|
652
|
-
|
653
|
-
|
654
|
-
|
700
|
+
if fill_first and not self._participants_filled:
|
701
|
+
async for _ in self.get_participants():
|
702
|
+
pass
|
703
|
+
if self.pk is not None:
|
704
|
+
with self.xmpp.store.session():
|
705
|
+
stored = self.__participants_store.get_by_nickname(
|
706
|
+
self.pk, nickname
|
707
|
+
) or self.__participants_store.get_by_resource(self.pk, nickname)
|
708
|
+
if stored is not None:
|
709
|
+
return self.Participant.from_store(self.session, stored)
|
655
710
|
|
656
711
|
if raise_if_not_found:
|
657
712
|
raise XMPPError("item-not-found")
|
658
713
|
p = self.Participant(self, nickname, **kwargs)
|
659
|
-
if store:
|
714
|
+
if store and not self._updating_info:
|
660
715
|
self.__store_participant(p)
|
661
716
|
if (
|
662
717
|
not self.get_lock("fill participants")
|
@@ -694,29 +749,43 @@ class LegacyMUC(
|
|
694
749
|
:return:
|
695
750
|
"""
|
696
751
|
await self.session.contacts.ready
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
self.
|
704
|
-
|
752
|
+
|
753
|
+
if self.pk is not None:
|
754
|
+
assert c.contact_pk is not None
|
755
|
+
with self.__store.session():
|
756
|
+
stored = self.__participants_store.get_by_contact(self.pk, c.contact_pk)
|
757
|
+
if stored is not None:
|
758
|
+
return self.Participant.from_store(
|
759
|
+
self.session, stored, muc=self, contact=c
|
760
|
+
)
|
705
761
|
|
706
762
|
nickname = c.name or _unescape_node(c.jid_username)
|
707
|
-
|
763
|
+
|
764
|
+
if self.pk is None:
|
765
|
+
nick_available = True
|
766
|
+
else:
|
767
|
+
nick_available = self.__store.nickname_is_available(self.pk, nickname)
|
768
|
+
|
769
|
+
if not nick_available:
|
708
770
|
self.log.debug("Nickname conflict")
|
709
771
|
nickname = f"{nickname} ({c.jid_username})"
|
710
772
|
p = self.Participant(self, nickname, **kwargs)
|
711
773
|
p.contact = c
|
712
774
|
|
775
|
+
if self._updating_info:
|
776
|
+
return p
|
777
|
+
|
778
|
+
self.__store_participant(p)
|
713
779
|
# FIXME: this is not great but given the current design,
|
714
780
|
# during participants fill and history backfill we do not
|
715
|
-
# want to send presence, because we might update affiliation
|
781
|
+
# want to send presence, because we might :update affiliation
|
716
782
|
# and role afterwards.
|
717
783
|
# We need a refactor of the MUC class… later™
|
718
|
-
|
719
|
-
|
784
|
+
if (
|
785
|
+
self._participants_filled
|
786
|
+
and not self.get_lock("fill participants")
|
787
|
+
and not self.get_lock("fill history")
|
788
|
+
):
|
720
789
|
p.send_last_presence(force=True, no_cache_online=True)
|
721
790
|
return p
|
722
791
|
|
@@ -729,21 +798,6 @@ class LegacyMUC(
|
|
729
798
|
return await self.get_user_participant(**kwargs)
|
730
799
|
return await self.get_participant_by_contact(c, **kwargs)
|
731
800
|
|
732
|
-
async def get_participants(self, fill_first=True):
|
733
|
-
"""
|
734
|
-
Get all known participants of the group, ensure :meth:`.LegacyMUC.fill_participants`
|
735
|
-
has been awaited once before. Plugins should not use that, internal
|
736
|
-
slidge use only.
|
737
|
-
:return:
|
738
|
-
"""
|
739
|
-
if fill_first:
|
740
|
-
await self.__fill_participants()
|
741
|
-
assert self.pk is not None
|
742
|
-
return [
|
743
|
-
self.Participant.from_store(self.session, s)
|
744
|
-
for s in self.__participants_store.get_all(self.pk)
|
745
|
-
]
|
746
|
-
|
747
801
|
def remove_participant(self, p: "LegacyParticipantType", kick=False, ban=False):
|
748
802
|
"""
|
749
803
|
Call this when a participant leaves the room
|
@@ -1029,10 +1083,9 @@ class LegacyMUC(
|
|
1029
1083
|
):
|
1030
1084
|
"""
|
1031
1085
|
Triggered when the user requests changing the affiliation of a contact
|
1032
|
-
for this group
|
1086
|
+
for this group.
|
1033
1087
|
|
1034
|
-
Examples: promotion them to moderator,
|
1035
|
-
ban (affiliation=outcast).
|
1088
|
+
Examples: promotion them to moderator, ban (affiliation=outcast).
|
1036
1089
|
|
1037
1090
|
:param contact: The contact whose affiliation change is requested
|
1038
1091
|
:param affiliation: The new affiliation
|
@@ -1041,6 +1094,16 @@ class LegacyMUC(
|
|
1041
1094
|
"""
|
1042
1095
|
raise NotImplementedError
|
1043
1096
|
|
1097
|
+
async def on_kick(self, contact: "LegacyContact", reason: Optional[str]):
|
1098
|
+
"""
|
1099
|
+
Triggered when the user requests changing the role of a contact
|
1100
|
+
to "none" for this group. Action commonly known as "kick".
|
1101
|
+
|
1102
|
+
:param contact: Contact to be kicked
|
1103
|
+
:param reason: A reason for this kick
|
1104
|
+
"""
|
1105
|
+
raise NotImplementedError
|
1106
|
+
|
1044
1107
|
async def on_set_config(
|
1045
1108
|
self,
|
1046
1109
|
name: Optional[str],
|
@@ -1127,6 +1190,7 @@ class LegacyMUC(
|
|
1127
1190
|
)
|
1128
1191
|
muc.pk = stored.id
|
1129
1192
|
muc.type = stored.muc_type # type: ignore
|
1193
|
+
muc.user_nick = stored.user_nick
|
1130
1194
|
if stored.name:
|
1131
1195
|
muc.DISCO_NAME = stored.name
|
1132
1196
|
if stored.description:
|
@@ -1137,17 +1201,11 @@ class LegacyMUC(
|
|
1137
1201
|
if stored.subject_date is not None:
|
1138
1202
|
muc._subject_date = stored.subject_date.replace(tzinfo=timezone.utc)
|
1139
1203
|
muc._participants_filled = stored.participants_filled
|
1140
|
-
muc.
|
1204
|
+
muc._n_participants = stored.n_participants
|
1205
|
+
muc._history_filled = stored.history_filled
|
1141
1206
|
if stored.user_resources is not None:
|
1142
1207
|
muc._user_resources = set(json.loads(stored.user_resources))
|
1143
|
-
|
1144
|
-
muc.subject_setter = (
|
1145
|
-
LegacyParticipant.get_self_or_unique_subclass().from_store(
|
1146
|
-
session,
|
1147
|
-
stored.subject_setter,
|
1148
|
-
muc=muc,
|
1149
|
-
)
|
1150
|
-
)
|
1208
|
+
muc._subject_setter = stored.subject_setter
|
1151
1209
|
muc.archive = MessageArchive(muc.pk, session.xmpp.store.mam)
|
1152
1210
|
muc._set_avatar_from_store(stored)
|
1153
1211
|
return muc
|
slidge/main.py
CHANGED
@@ -17,6 +17,7 @@ import asyncio
|
|
17
17
|
import importlib
|
18
18
|
import logging
|
19
19
|
import os
|
20
|
+
import re
|
20
21
|
import signal
|
21
22
|
from pathlib import Path
|
22
23
|
|
@@ -49,7 +50,7 @@ class MainConfig(ConfigModule):
|
|
49
50
|
args.home_dir = Path("/var/lib/slidge") / str(args.jid)
|
50
51
|
|
51
52
|
if args.user_jid_validator is None:
|
52
|
-
args.user_jid_validator = ".*@" + args.server
|
53
|
+
args.user_jid_validator = ".*@" + re.escape(args.server)
|
53
54
|
|
54
55
|
if args.db_url is None:
|
55
56
|
args.db_url = f"sqlite:///{args.home_dir}/slidge.sqlite"
|
@@ -116,8 +117,6 @@ def configure():
|
|
116
117
|
db_file = config.HOME_DIR / "slidge.db"
|
117
118
|
user_store.set_file(db_file, args.secret_key)
|
118
119
|
|
119
|
-
avatar_cache.set_dir(h / "slidge_avatars_v3")
|
120
|
-
|
121
120
|
config.UPLOAD_REQUESTER = config.UPLOAD_REQUESTER or config.JID.bare
|
122
121
|
|
123
122
|
return unknown_argv
|
@@ -161,6 +160,7 @@ def main():
|
|
161
160
|
gateway: BaseGateway = BaseGateway.get_unique_subclass()()
|
162
161
|
avatar_cache.http = gateway.http
|
163
162
|
avatar_cache.store = gateway.store.avatars
|
163
|
+
avatar_cache.set_dir(config.HOME_DIR / "slidge_avatars_v3")
|
164
164
|
avatar_cache.legacy_avatar_type = gateway.AVATAR_ID_TYPE
|
165
165
|
|
166
166
|
PepAvatar.store = gateway.store
|
@@ -57,6 +57,8 @@ class VCard4Provider(BasePlugin):
|
|
57
57
|
if not hasattr(self.xmpp, "get_session_from_jid"):
|
58
58
|
return None
|
59
59
|
jid = JID(jid)
|
60
|
+
if not jid.local:
|
61
|
+
return None
|
60
62
|
requested_by = JID(requested_by)
|
61
63
|
session = self.xmpp.get_session_from_jid(requested_by)
|
62
64
|
if session is None:
|
slidge/util/archive_msg.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
from copy import copy
|
2
2
|
from datetime import datetime, timezone
|
3
3
|
from typing import Optional, Union
|
4
|
+
from uuid import uuid4
|
4
5
|
from xml.etree import ElementTree as ET
|
5
6
|
|
6
7
|
from slixmpp import Message
|
@@ -30,7 +31,7 @@ class HistoryMessage:
|
|
30
31
|
else:
|
31
32
|
from_db = False
|
32
33
|
|
33
|
-
self.id = stanza["stanza_id"]["id"]
|
34
|
+
self.id = stanza["stanza_id"]["id"] or uuid4().hex
|
34
35
|
self.when: datetime = (
|
35
36
|
when or stanza["delay"]["stamp"] or datetime.now(tz=timezone.utc)
|
36
37
|
)
|