slidge 0.1.2__py3-none-any.whl → 0.2.0a0__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 +5 -6
- slidge/command/base.py +1 -2
- slidge/command/register.py +32 -16
- slidge/command/user.py +85 -5
- slidge/contact/contact.py +93 -31
- slidge/contact/roster.py +54 -39
- slidge/core/config.py +13 -7
- slidge/core/gateway/base.py +139 -34
- slidge/core/gateway/disco.py +2 -4
- slidge/core/gateway/mam.py +1 -4
- 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 +109 -51
- slidge/core/gateway/vcard_temp.py +6 -4
- slidge/core/mixins/__init__.py +11 -1
- slidge/core/mixins/attachment.py +15 -10
- slidge/core/mixins/avatar.py +66 -18
- slidge/core/mixins/base.py +8 -2
- slidge/core/mixins/message.py +11 -7
- slidge/core/mixins/message_maker.py +17 -9
- slidge/core/mixins/presence.py +14 -4
- slidge/core/pubsub.py +54 -212
- slidge/core/session.py +65 -33
- 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/29f5280c61aa_store_subject_setter_in_room.py +37 -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 +76 -0
- slidge/db/alembic/versions/b33993e87db3_move_everything_to_persistent_db.py +214 -0
- slidge/db/alembic/versions/e91195719c2c_store_users_avatars_persistently.py +26 -0
- slidge/db/avatar.py +224 -0
- slidge/db/meta.py +65 -0
- slidge/db/models.py +365 -0
- slidge/db/store.py +976 -0
- slidge/group/archive.py +13 -14
- slidge/group/bookmarks.py +59 -56
- slidge/group/participant.py +81 -29
- slidge/group/room.py +242 -142
- 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 +1 -0
- slidge/util/db.py +1 -47
- slidge/util/test.py +21 -4
- slidge/util/types.py +24 -4
- {slidge-0.1.2.dist-info → slidge-0.2.0a0.dist-info}/METADATA +3 -1
- slidge-0.2.0a0.dist-info/RECORD +108 -0
- slidge/core/cache.py +0 -183
- slidge/util/schema.sql +0 -126
- slidge/util/sql.py +0 -508
- slidge-0.1.2.dist-info/RECORD +0 -96
- {slidge-0.1.2.dist-info → slidge-0.2.0a0.dist-info}/LICENSE +0 -0
- {slidge-0.1.2.dist-info → slidge-0.2.0a0.dist-info}/WHEEL +0 -0
- {slidge-0.1.2.dist-info → slidge-0.2.0a0.dist-info}/entry_points.txt +0 -0
slidge/group/room.py
CHANGED
@@ -1,10 +1,11 @@
|
|
1
|
+
import json
|
1
2
|
import logging
|
2
3
|
import re
|
3
4
|
import string
|
4
5
|
import warnings
|
5
6
|
from copy import copy
|
6
7
|
from datetime import datetime, timedelta, timezone
|
7
|
-
from typing import TYPE_CHECKING, Generic, Optional, Union
|
8
|
+
from typing import TYPE_CHECKING, Generic, Optional, Self, Union
|
8
9
|
from uuid import uuid4
|
9
10
|
|
10
11
|
from slixmpp import JID, Iq, Message, Presence
|
@@ -15,12 +16,15 @@ from slixmpp.plugins.xep_0060.stanza import Item
|
|
15
16
|
from slixmpp.plugins.xep_0082 import parse as str_to_datetime
|
16
17
|
from slixmpp.xmlstream import ET
|
17
18
|
|
19
|
+
from ..contact.contact import LegacyContact
|
18
20
|
from ..contact.roster import ContactIsUser
|
19
21
|
from ..core import config
|
22
|
+
from ..core.mixins import StoredAttributeMixin
|
20
23
|
from ..core.mixins.avatar import AvatarMixin
|
21
24
|
from ..core.mixins.disco import ChatterDiscoMixin
|
22
25
|
from ..core.mixins.lock import NamedLockMixin
|
23
26
|
from ..core.mixins.recipient import ReactionRecipientMixin, ThreadRecipientMixin
|
27
|
+
from ..db.models import Room
|
24
28
|
from ..util import ABCSubclassableOnceAtMost
|
25
29
|
from ..util.types import (
|
26
30
|
LegacyGroupIdType,
|
@@ -33,9 +37,9 @@ from ..util.types import (
|
|
33
37
|
)
|
34
38
|
from ..util.util import deprecated
|
35
39
|
from .archive import MessageArchive
|
40
|
+
from .participant import LegacyParticipant
|
36
41
|
|
37
42
|
if TYPE_CHECKING:
|
38
|
-
from ..contact import LegacyContact
|
39
43
|
from ..core.gateway import BaseGateway
|
40
44
|
from ..core.session import BaseSession
|
41
45
|
|
@@ -46,6 +50,7 @@ class LegacyMUC(
|
|
46
50
|
Generic[
|
47
51
|
LegacyGroupIdType, LegacyMessageType, LegacyParticipantType, LegacyUserIdType
|
48
52
|
],
|
53
|
+
StoredAttributeMixin,
|
49
54
|
AvatarMixin,
|
50
55
|
NamedLockMixin,
|
51
56
|
ChatterDiscoMixin,
|
@@ -60,8 +65,6 @@ class LegacyMUC(
|
|
60
65
|
on the user's :py:class:`slidge.core.session.BaseSession`.
|
61
66
|
"""
|
62
67
|
|
63
|
-
subject_date: Optional[datetime] = None
|
64
|
-
n_participants: Optional[int] = None
|
65
68
|
max_history_fetch = 100
|
66
69
|
|
67
70
|
type = MucType.CHANNEL
|
@@ -119,67 +122,99 @@ class LegacyMUC(
|
|
119
122
|
|
120
123
|
_avatar_pubsub_broadcast = False
|
121
124
|
_avatar_bare_jid = True
|
125
|
+
archive: MessageArchive
|
122
126
|
|
123
127
|
def __init__(self, session: "BaseSession", legacy_id: LegacyGroupIdType, jid: JID):
|
124
|
-
from .participant import LegacyParticipant
|
125
|
-
|
126
128
|
self.session = session
|
127
129
|
self.xmpp: "BaseGateway" = session.xmpp
|
128
|
-
self.
|
129
|
-
self.log = logging.getLogger(f"{self.user.bare_jid}:muc:{jid}")
|
130
|
+
self.log = logging.getLogger(f"{self.user_jid.bare}:muc:{jid}")
|
130
131
|
|
131
132
|
self.legacy_id = legacy_id
|
132
133
|
self.jid = jid
|
133
134
|
|
134
|
-
self.
|
135
|
+
self._user_resources = set[str]()
|
135
136
|
|
136
137
|
self.Participant = LegacyParticipant.get_self_or_unique_subclass()
|
137
138
|
|
138
|
-
self.xmpp.add_event_handler(
|
139
|
-
"presence_unavailable", self._on_presence_unavailable
|
140
|
-
)
|
141
|
-
|
142
139
|
self._subject = ""
|
143
|
-
self.
|
144
|
-
|
140
|
+
self._subject_setter: Union[str, None, "LegacyContact", "LegacyParticipant"] = (
|
141
|
+
None
|
145
142
|
)
|
146
143
|
|
147
|
-
self.
|
144
|
+
self.pk: Optional[int] = None
|
148
145
|
self._user_nick: Optional[str] = None
|
149
146
|
|
150
|
-
self.
|
151
|
-
self._participants_by_escaped_nicknames = dict[str, LegacyParticipantType]()
|
152
|
-
self._participants_by_contacts = dict["LegacyContact", LegacyParticipantType]()
|
153
|
-
|
154
|
-
self.__participants_filled = False
|
147
|
+
self._participants_filled = False
|
155
148
|
self.__history_filled = False
|
156
149
|
self._description = ""
|
150
|
+
self._subject_date: Optional[datetime] = None
|
151
|
+
|
152
|
+
self.__participants_store = self.xmpp.store.participants
|
153
|
+
self.__store = self.xmpp.store.rooms
|
154
|
+
|
155
|
+
self._n_participants: Optional[int] = None
|
156
|
+
|
157
157
|
super().__init__()
|
158
158
|
|
159
|
+
@property
|
160
|
+
def n_participants(self):
|
161
|
+
return self._n_participants
|
162
|
+
|
163
|
+
@n_participants.setter
|
164
|
+
def n_participants(self, n_participants: Optional[int]):
|
165
|
+
if self._n_participants == n_participants:
|
166
|
+
return
|
167
|
+
self._n_participants = n_participants
|
168
|
+
assert self.pk is not None
|
169
|
+
self.__store.update_n_participants(self.pk, n_participants)
|
170
|
+
|
171
|
+
@property
|
172
|
+
def user_jid(self):
|
173
|
+
return self.session.user_jid
|
174
|
+
|
159
175
|
def __repr__(self):
|
160
176
|
return f"<MUC {self.legacy_id}/{self.jid}/{self.name}>"
|
161
177
|
|
178
|
+
@property
|
179
|
+
def subject_date(self) -> Optional[datetime]:
|
180
|
+
return self._subject_date
|
181
|
+
|
182
|
+
@subject_date.setter
|
183
|
+
def subject_date(self, when: Optional[datetime]) -> None:
|
184
|
+
self._subject_date = when
|
185
|
+
assert self.pk is not None
|
186
|
+
self.__store.update_subject_date(self.pk, when)
|
187
|
+
|
162
188
|
def __send_configuration_change(self, codes):
|
163
189
|
part = self.get_system_participant()
|
164
190
|
part.send_configuration_change(codes)
|
165
191
|
|
166
192
|
@property
|
167
193
|
def user_nick(self):
|
168
|
-
return
|
169
|
-
self._user_nick
|
170
|
-
or self.session.bookmarks.user_nick
|
171
|
-
or self.session.user.jid.node
|
172
|
-
)
|
194
|
+
return self._user_nick or self.session.bookmarks.user_nick or self.user_jid.node
|
173
195
|
|
174
196
|
@user_nick.setter
|
175
197
|
def user_nick(self, nick: str):
|
176
198
|
self._user_nick = nick
|
177
199
|
|
200
|
+
def add_user_resource(self, resource: str) -> None:
|
201
|
+
self._user_resources.add(resource)
|
202
|
+
assert self.pk is not None
|
203
|
+
self.__store.set_resource(self.pk, self._user_resources)
|
204
|
+
|
205
|
+
def get_user_resources(self) -> set[str]:
|
206
|
+
return self._user_resources
|
207
|
+
|
208
|
+
def remove_user_resource(self, resource: str) -> None:
|
209
|
+
self._user_resources.remove(resource)
|
210
|
+
assert self.pk is not None
|
211
|
+
self.__store.set_resource(self.pk, self._user_resources)
|
212
|
+
|
178
213
|
async def __fill_participants(self):
|
179
214
|
async with self.lock("fill participants"):
|
180
|
-
if self.
|
215
|
+
if self._participants_filled:
|
181
216
|
return
|
182
|
-
self.
|
217
|
+
self._participants_filled = True
|
183
218
|
try:
|
184
219
|
await self.fill_participants()
|
185
220
|
except NotImplementedError:
|
@@ -230,22 +265,24 @@ class LegacyMUC(
|
|
230
265
|
if self._description == d:
|
231
266
|
return
|
232
267
|
self._description = d
|
268
|
+
assert self.pk is not None
|
269
|
+
self.__store.update_description(self.pk, d)
|
233
270
|
self.__send_configuration_change((104,))
|
234
271
|
|
235
|
-
def
|
272
|
+
def on_presence_unavailable(self, p: Presence):
|
236
273
|
pto = p.get_to()
|
237
274
|
if pto.bare != self.jid.bare:
|
238
275
|
return
|
239
276
|
|
240
277
|
pfrom = p.get_from()
|
241
|
-
if pfrom.bare != self.
|
278
|
+
if pfrom.bare != self.user_jid.bare:
|
242
279
|
return
|
243
|
-
if (resource := pfrom.resource) in
|
280
|
+
if (resource := pfrom.resource) in self._user_resources:
|
244
281
|
if pto.resource != self.user_nick:
|
245
282
|
self.log.debug(
|
246
283
|
"Received 'leave group' request but with wrong nickname. %s", p
|
247
284
|
)
|
248
|
-
|
285
|
+
self.remove_user_resource(resource)
|
249
286
|
else:
|
250
287
|
self.log.debug(
|
251
288
|
"Received 'leave group' request but resource was not listed. %s", p
|
@@ -300,7 +337,7 @@ class LegacyMUC(
|
|
300
337
|
def subject(self, s: str):
|
301
338
|
if s == self._subject:
|
302
339
|
return
|
303
|
-
self.
|
340
|
+
self.session.create_task(
|
304
341
|
self.__get_subject_setter_participant()
|
305
342
|
).add_done_callback(
|
306
343
|
lambda task: task.result().set_room_subject(
|
@@ -308,16 +345,23 @@ class LegacyMUC(
|
|
308
345
|
)
|
309
346
|
)
|
310
347
|
self._subject = s
|
348
|
+
assert self.pk is not None
|
349
|
+
self.__store.update_subject(self.pk, s)
|
311
350
|
|
312
351
|
@property
|
313
352
|
def is_anonymous(self):
|
314
353
|
return self.type == MucType.CHANNEL
|
315
354
|
|
316
|
-
|
317
|
-
|
355
|
+
@property
|
356
|
+
def subject_setter(self):
|
357
|
+
return self._subject_setter
|
318
358
|
|
319
|
-
|
359
|
+
@subject_setter.setter
|
360
|
+
def subject_setter(self, subject_setter):
|
361
|
+
self._subject_setter = subject_setter
|
362
|
+
self.__store.update(self)
|
320
363
|
|
364
|
+
async def __get_subject_setter_participant(self):
|
321
365
|
who = self.subject_setter
|
322
366
|
|
323
367
|
if isinstance(who, LegacyParticipant):
|
@@ -341,6 +385,7 @@ class LegacyMUC(
|
|
341
385
|
"vcard-temp",
|
342
386
|
"urn:xmpp:ping",
|
343
387
|
"urn:xmpp:occupant-id:0",
|
388
|
+
"jabber:iq:register",
|
344
389
|
self.xmpp.plugin["xep_0425"].stanza.NS,
|
345
390
|
]
|
346
391
|
if self.type == MucType.GROUP:
|
@@ -364,10 +409,10 @@ class LegacyMUC(
|
|
364
409
|
form.add_field("muc#maxhistoryfetch", value=str(self.max_history_fetch))
|
365
410
|
form.add_field("muc#roominfo_subjectmod", "boolean", value=False)
|
366
411
|
|
367
|
-
if self._ALL_INFO_FILLED_ON_STARTUP or self.
|
412
|
+
if self._ALL_INFO_FILLED_ON_STARTUP or self._participants_filled:
|
368
413
|
n: Optional[int] = len(await self.get_participants())
|
369
414
|
else:
|
370
|
-
n = self.
|
415
|
+
n = self._n_participants
|
371
416
|
if n is not None:
|
372
417
|
form.add_field("muc#roominfo_occupants", value=str(n))
|
373
418
|
|
@@ -415,8 +460,8 @@ class LegacyMUC(
|
|
415
460
|
presence.send()
|
416
461
|
|
417
462
|
def user_full_jids(self):
|
418
|
-
for r in self.
|
419
|
-
j = copy(self.
|
463
|
+
for r in self._user_resources:
|
464
|
+
j = copy(self.user_jid)
|
420
465
|
j.resource = r
|
421
466
|
yield j
|
422
467
|
|
@@ -427,9 +472,9 @@ class LegacyMUC(
|
|
427
472
|
return user_muc_jid
|
428
473
|
|
429
474
|
def _legacy_to_xmpp(self, legacy_id: LegacyMessageType):
|
430
|
-
return self.
|
431
|
-
legacy_id
|
432
|
-
)
|
475
|
+
return self.xmpp.store.sent.get_group_xmpp_id(
|
476
|
+
self.session.user_pk, str(legacy_id)
|
477
|
+
) or self.session.legacy_to_xmpp_msg_id(legacy_id)
|
433
478
|
|
434
479
|
async def echo(
|
435
480
|
self, msg: Message, legacy_msg_id: Optional[LegacyMessageType] = None
|
@@ -458,7 +503,14 @@ class LegacyMUC(
|
|
458
503
|
|
459
504
|
msg.send()
|
460
505
|
|
506
|
+
def _get_cached_avatar_id(self):
|
507
|
+
assert self.pk is not None
|
508
|
+
return self.xmpp.store.rooms.get_avatar_legacy_id(self.pk)
|
509
|
+
|
461
510
|
def _post_avatar_update(self) -> None:
|
511
|
+
if self.pk is None or self._avatar_pk is None:
|
512
|
+
return
|
513
|
+
self.xmpp.store.rooms.set_avatar(self.pk, self._avatar_pk)
|
462
514
|
self.__send_configuration_change((104,))
|
463
515
|
self._send_room_presence()
|
464
516
|
|
@@ -480,10 +532,10 @@ class LegacyMUC(
|
|
480
532
|
requested_nickname = join_presence.get_to().resource
|
481
533
|
client_resource = user_full_jid.resource
|
482
534
|
|
483
|
-
if client_resource in self.
|
535
|
+
if client_resource in self._user_resources:
|
484
536
|
self.log.debug("Received join from a resource that is already joined.")
|
485
537
|
|
486
|
-
self.
|
538
|
+
self.add_user_resource(client_resource)
|
487
539
|
|
488
540
|
if not requested_nickname or not client_resource:
|
489
541
|
raise XMPPError("jid-malformed", by=self.jid)
|
@@ -491,18 +543,18 @@ class LegacyMUC(
|
|
491
543
|
self.log.debug(
|
492
544
|
"Resource %s of %s wants to join room %s with nickname %s",
|
493
545
|
client_resource,
|
494
|
-
self.
|
546
|
+
self.user_jid,
|
495
547
|
self.legacy_id,
|
496
548
|
requested_nickname,
|
497
549
|
)
|
498
550
|
|
499
551
|
await self.__fill_participants()
|
500
552
|
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
553
|
+
assert self.pk is not None
|
554
|
+
for db_participant in self.xmpp.store.participants.get_all(
|
555
|
+
self.pk, user_included=False
|
556
|
+
):
|
557
|
+
participant = self.Participant.from_store(self.session, db_participant)
|
506
558
|
participant.send_initial_presence(full_jid=user_full_jid)
|
507
559
|
|
508
560
|
user_nick = self.user_nick
|
@@ -558,13 +610,13 @@ class LegacyMUC(
|
|
558
610
|
self.__store_participant(p)
|
559
611
|
return p
|
560
612
|
|
561
|
-
def __store_participant(self, p: "LegacyParticipantType"):
|
613
|
+
def __store_participant(self, p: "LegacyParticipantType") -> None:
|
562
614
|
# we don't want to update the participant list when we're filling history
|
563
615
|
if not self.KEEP_BACKFILLED_PARTICIPANTS and self.get_lock("fill history"):
|
564
616
|
return
|
565
|
-
self.
|
566
|
-
|
567
|
-
|
617
|
+
assert self.pk is not None
|
618
|
+
p.pk = self.__participants_store.add(self.pk, p.nickname)
|
619
|
+
self.__participants_store.update(p)
|
568
620
|
|
569
621
|
async def get_participant(
|
570
622
|
self,
|
@@ -593,23 +645,27 @@ class LegacyMUC(
|
|
593
645
|
"""
|
594
646
|
if fill_first:
|
595
647
|
await self.__fill_participants()
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
)
|
612
|
-
|
648
|
+
assert self.pk is not None
|
649
|
+
with self.xmpp.store.session():
|
650
|
+
stored = self.__participants_store.get_by_nickname(
|
651
|
+
self.pk, nickname
|
652
|
+
) or self.__participants_store.get_by_resource(self.pk, nickname)
|
653
|
+
if stored is not None:
|
654
|
+
return self.Participant.from_store(self.session, stored)
|
655
|
+
|
656
|
+
if raise_if_not_found:
|
657
|
+
raise XMPPError("item-not-found")
|
658
|
+
p = self.Participant(self, nickname, **kwargs)
|
659
|
+
if store:
|
660
|
+
self.__store_participant(p)
|
661
|
+
if (
|
662
|
+
not self.get_lock("fill participants")
|
663
|
+
and not self.get_lock("fill history")
|
664
|
+
and self._participants_filled
|
665
|
+
and not p.is_user
|
666
|
+
and not p.is_system
|
667
|
+
):
|
668
|
+
p.send_affiliation_change()
|
613
669
|
return p
|
614
670
|
|
615
671
|
def get_system_participant(self) -> "LegacyParticipantType":
|
@@ -638,25 +694,30 @@ class LegacyMUC(
|
|
638
694
|
:return:
|
639
695
|
"""
|
640
696
|
await self.session.contacts.ready
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
697
|
+
assert self.pk is not None
|
698
|
+
assert c.contact_pk is not None
|
699
|
+
with self.__store.session():
|
700
|
+
stored = self.__participants_store.get_by_contact(self.pk, c.contact_pk)
|
701
|
+
if stored is not None:
|
702
|
+
return self.Participant.from_store(
|
703
|
+
self.session, stored, muc=self, contact=c
|
704
|
+
)
|
705
|
+
|
706
|
+
nickname = c.name or _unescape_node(c.jid_username)
|
707
|
+
if not self.__store.nickname_is_available(self.pk, nickname):
|
708
|
+
self.log.debug("Nickname conflict")
|
709
|
+
nickname = f"{nickname} ({c.jid_username})"
|
710
|
+
p = self.Participant(self, nickname, **kwargs)
|
711
|
+
p.contact = c
|
712
|
+
|
713
|
+
# FIXME: this is not great but given the current design,
|
714
|
+
# during participants fill and history backfill we do not
|
715
|
+
# want to send presence, because we might update affiliation
|
716
|
+
# and role afterwards.
|
717
|
+
# We need a refactor of the MUC class… later™
|
718
|
+
self.__store_participant(p)
|
719
|
+
if not self.get_lock("fill participants") and not self.get_lock("fill history"):
|
720
|
+
p.send_last_presence(force=True, no_cache_online=True)
|
660
721
|
return p
|
661
722
|
|
662
723
|
async def get_participant_by_legacy_id(
|
@@ -668,15 +729,20 @@ class LegacyMUC(
|
|
668
729
|
return await self.get_user_participant(**kwargs)
|
669
730
|
return await self.get_participant_by_contact(c, **kwargs)
|
670
731
|
|
671
|
-
async def get_participants(self):
|
732
|
+
async def get_participants(self, fill_first=True):
|
672
733
|
"""
|
673
734
|
Get all known participants of the group, ensure :meth:`.LegacyMUC.fill_participants`
|
674
735
|
has been awaited once before. Plugins should not use that, internal
|
675
736
|
slidge use only.
|
676
737
|
:return:
|
677
738
|
"""
|
678
|
-
|
679
|
-
|
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
|
+
]
|
680
746
|
|
681
747
|
def remove_participant(self, p: "LegacyParticipantType", kick=False, ban=False):
|
682
748
|
"""
|
@@ -688,19 +754,7 @@ class LegacyMUC(
|
|
688
754
|
"""
|
689
755
|
if kick and ban:
|
690
756
|
raise TypeError("Either kick or ban")
|
691
|
-
|
692
|
-
try:
|
693
|
-
del self._participants_by_contacts[p.contact]
|
694
|
-
except KeyError:
|
695
|
-
self.log.warning(
|
696
|
-
"Removed a participant we didn't know was here?, %s", p
|
697
|
-
)
|
698
|
-
else:
|
699
|
-
p.contact.participants.remove(p)
|
700
|
-
try:
|
701
|
-
del self._participants_by_nicknames[p.nickname] # type:ignore
|
702
|
-
except KeyError:
|
703
|
-
self.log.warning("Removed a participant we didn't know was here?, %s", p)
|
757
|
+
self.__participants_store.delete(p.pk)
|
704
758
|
if kick:
|
705
759
|
codes = {307}
|
706
760
|
elif ban:
|
@@ -713,14 +767,15 @@ class LegacyMUC(
|
|
713
767
|
p._send(presence)
|
714
768
|
|
715
769
|
def rename_participant(self, old_nickname: str, new_nickname: str):
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
p.nickname
|
770
|
+
assert self.pk is not None
|
771
|
+
with self.xmpp.store.session():
|
772
|
+
stored = self.__participants_store.get_by_nickname(self.pk, old_nickname)
|
773
|
+
if stored is None:
|
774
|
+
self.log.debug("Tried to rename a participant that we didn't know")
|
775
|
+
return
|
776
|
+
p = self.Participant.from_store(self.session, stored)
|
777
|
+
if p.nickname == old_nickname:
|
778
|
+
p.nickname = new_nickname
|
724
779
|
|
725
780
|
async def __old_school_history(
|
726
781
|
self,
|
@@ -849,7 +904,7 @@ class LegacyMUC(
|
|
849
904
|
|
850
905
|
:param r: The resource to kick
|
851
906
|
"""
|
852
|
-
pto = self.
|
907
|
+
pto = self.user_jid
|
853
908
|
pto.resource = r
|
854
909
|
p = self.xmpp.make_presence(
|
855
910
|
pfrom=(await self.get_user_participant()).jid, pto=pto
|
@@ -882,7 +937,7 @@ class LegacyMUC(
|
|
882
937
|
item = Item()
|
883
938
|
item["id"] = self.jid
|
884
939
|
|
885
|
-
iq = Iq(stype="get", sfrom=self.
|
940
|
+
iq = Iq(stype="get", sfrom=self.user_jid, sto=self.user_jid)
|
886
941
|
iq["pubsub"]["items"]["node"] = self.xmpp["xep_0402"].stanza.NS
|
887
942
|
iq["pubsub"]["items"].append(item)
|
888
943
|
|
@@ -912,7 +967,7 @@ class LegacyMUC(
|
|
912
967
|
item["conference"]["autojoin"] = auto_join
|
913
968
|
|
914
969
|
item["conference"]["nick"] = self.user_nick
|
915
|
-
iq = Iq(stype="set", sfrom=self.
|
970
|
+
iq = Iq(stype="set", sfrom=self.user_jid, sto=self.user_jid)
|
916
971
|
iq["pubsub"]["publish"]["node"] = self.xmpp["xep_0402"].stanza.NS
|
917
972
|
iq["pubsub"]["publish"].append(item)
|
918
973
|
|
@@ -1015,30 +1070,39 @@ class LegacyMUC(
|
|
1015
1070
|
raise NotImplementedError
|
1016
1071
|
|
1017
1072
|
async def parse_mentions(self, text: str) -> list[Mention]:
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1073
|
+
with self.__store.session():
|
1074
|
+
await self.__fill_participants()
|
1075
|
+
assert self.pk is not None
|
1076
|
+
participants = {
|
1077
|
+
p.nickname: p for p in self.__participants_store.get_all(self.pk)
|
1078
|
+
}
|
1079
|
+
|
1080
|
+
if len(participants) == 0:
|
1081
|
+
return []
|
1082
|
+
|
1083
|
+
result = []
|
1084
|
+
for match in re.finditer(
|
1085
|
+
"|".join(
|
1086
|
+
sorted(
|
1087
|
+
[re.escape(nick) for nick in participants.keys()],
|
1088
|
+
key=lambda nick: len(nick),
|
1089
|
+
reverse=True,
|
1090
|
+
)
|
1091
|
+
),
|
1092
|
+
text,
|
1093
|
+
):
|
1094
|
+
span = match.span()
|
1095
|
+
nick = match.group()
|
1096
|
+
if span[0] != 0 and text[span[0] - 1] not in _WHITESPACE_OR_PUNCTUATION:
|
1097
|
+
continue
|
1098
|
+
if span[1] == len(text) or text[span[1]] in _WHITESPACE_OR_PUNCTUATION:
|
1099
|
+
participant = self.Participant.from_store(
|
1100
|
+
self.session, participants[nick]
|
1101
|
+
)
|
1102
|
+
if contact := participant.contact:
|
1103
|
+
result.append(
|
1104
|
+
Mention(contact=contact, start=span[0], end=span[1])
|
1105
|
+
)
|
1042
1106
|
return result
|
1043
1107
|
|
1044
1108
|
async def on_set_subject(self, subject: str) -> None:
|
@@ -1052,6 +1116,42 @@ class LegacyMUC(
|
|
1052
1116
|
"""
|
1053
1117
|
raise NotImplementedError
|
1054
1118
|
|
1119
|
+
@classmethod
|
1120
|
+
def from_store(cls, session, stored: Room, *args, **kwargs) -> Self:
|
1121
|
+
muc = cls(
|
1122
|
+
session,
|
1123
|
+
cls.xmpp.LEGACY_ROOM_ID_TYPE(stored.legacy_id),
|
1124
|
+
stored.jid,
|
1125
|
+
*args, # type: ignore
|
1126
|
+
**kwargs, # type: ignore
|
1127
|
+
)
|
1128
|
+
muc.pk = stored.id
|
1129
|
+
muc.type = stored.muc_type # type: ignore
|
1130
|
+
if stored.name:
|
1131
|
+
muc.DISCO_NAME = stored.name
|
1132
|
+
if stored.description:
|
1133
|
+
muc._description = stored.description
|
1134
|
+
if (data := stored.extra_attributes) is not None:
|
1135
|
+
muc.deserialize_extra_attributes(data)
|
1136
|
+
muc._subject = stored.subject or ""
|
1137
|
+
if stored.subject_date is not None:
|
1138
|
+
muc._subject_date = stored.subject_date.replace(tzinfo=timezone.utc)
|
1139
|
+
muc._participants_filled = stored.participants_filled
|
1140
|
+
muc.__history_filled = True
|
1141
|
+
if stored.user_resources is not None:
|
1142
|
+
muc._user_resources = set(json.loads(stored.user_resources))
|
1143
|
+
if stored.subject_setter is not None:
|
1144
|
+
muc.subject_setter = (
|
1145
|
+
LegacyParticipant.get_self_or_unique_subclass().from_store(
|
1146
|
+
session,
|
1147
|
+
stored.subject_setter,
|
1148
|
+
muc=muc,
|
1149
|
+
)
|
1150
|
+
)
|
1151
|
+
muc.archive = MessageArchive(muc.pk, session.xmpp.store.mam)
|
1152
|
+
muc._set_avatar_from_store(stored)
|
1153
|
+
return muc
|
1154
|
+
|
1055
1155
|
|
1056
1156
|
def set_origin_id(msg: Message, origin_id: str):
|
1057
1157
|
sub = ET.Element("{urn:xmpp:sid:0}origin-id")
|