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/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
|
)
|