slidge 0.1.3__py3-none-any.whl → 0.2.0a0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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 +100 -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 +77 -25
- 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.3.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.3.dist-info/RECORD +0 -96
- {slidge-0.1.3.dist-info → slidge-0.2.0a0.dist-info}/LICENSE +0 -0
- {slidge-0.1.3.dist-info → slidge-0.2.0a0.dist-info}/WHEEL +0 -0
- {slidge-0.1.3.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")
|