slidge 0.1.3__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 +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/archive.py
CHANGED
@@ -1,24 +1,22 @@
|
|
1
1
|
import logging
|
2
2
|
import uuid
|
3
3
|
from copy import copy
|
4
|
-
from datetime import datetime
|
4
|
+
from datetime import datetime, timezone
|
5
5
|
from typing import TYPE_CHECKING, Collection, Optional
|
6
6
|
|
7
7
|
from slixmpp import Iq, Message
|
8
8
|
|
9
|
+
from ..db.store import MAMStore
|
9
10
|
from ..util.archive_msg import HistoryMessage
|
10
|
-
from ..util.db import GatewayUser
|
11
|
-
from ..util.sql import db
|
12
11
|
|
13
12
|
if TYPE_CHECKING:
|
14
13
|
from .participant import LegacyParticipant
|
15
14
|
|
16
15
|
|
17
16
|
class MessageArchive:
|
18
|
-
def __init__(self,
|
19
|
-
self.
|
20
|
-
self.
|
21
|
-
db.mam_add_muc(db_id, user)
|
17
|
+
def __init__(self, room_pk: int, store: MAMStore):
|
18
|
+
self.room_pk = room_pk
|
19
|
+
self.__store = store
|
22
20
|
|
23
21
|
def add(
|
24
22
|
self,
|
@@ -40,7 +38,7 @@ class MessageArchive:
|
|
40
38
|
if participant.contact:
|
41
39
|
new_msg["muc"]["jid"] = participant.contact.jid.bare
|
42
40
|
elif participant.is_user:
|
43
|
-
new_msg["muc"]["jid"] = participant.
|
41
|
+
new_msg["muc"]["jid"] = participant.user_jid.bare
|
44
42
|
elif participant.is_system:
|
45
43
|
new_msg["muc"]["jid"] = participant.muc.jid
|
46
44
|
else:
|
@@ -49,7 +47,7 @@ class MessageArchive:
|
|
49
47
|
"jid"
|
50
48
|
] = f"{uuid.uuid4()}@{participant.xmpp.boundjid.bare}"
|
51
49
|
|
52
|
-
|
50
|
+
self.__store.add_message(self.room_pk, HistoryMessage(new_msg))
|
53
51
|
|
54
52
|
def __iter__(self):
|
55
53
|
return iter(self.get_all())
|
@@ -65,9 +63,8 @@ class MessageArchive:
|
|
65
63
|
sender: Optional[str] = None,
|
66
64
|
flip=False,
|
67
65
|
):
|
68
|
-
for msg in
|
69
|
-
self.
|
70
|
-
self.db_id,
|
66
|
+
for msg in self.__store.get_messages(
|
67
|
+
self.room_pk,
|
71
68
|
before_id=before_id,
|
72
69
|
after_id=after_id,
|
73
70
|
ids=ids,
|
@@ -87,11 +84,13 @@ class MessageArchive:
|
|
87
84
|
:return:
|
88
85
|
"""
|
89
86
|
reply = iq.reply()
|
90
|
-
messages =
|
87
|
+
messages = self.__store.get_first_and_last(self.room_pk)
|
91
88
|
if messages:
|
92
89
|
for x, m in [("start", messages[0]), ("end", messages[-1])]:
|
93
90
|
reply["mam_metadata"][x]["id"] = m.id
|
94
|
-
reply["mam_metadata"][x]["timestamp"] = m.sent_on
|
91
|
+
reply["mam_metadata"][x]["timestamp"] = m.sent_on.replace(
|
92
|
+
tzinfo=timezone.utc
|
93
|
+
)
|
95
94
|
else:
|
96
95
|
reply.enable("mam_metadata")
|
97
96
|
reply.send()
|
slidge/group/bookmarks.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import abc
|
2
2
|
import logging
|
3
|
-
from typing import TYPE_CHECKING, Generic, Type
|
3
|
+
from typing import TYPE_CHECKING, Generic, Iterator, Optional, Type
|
4
4
|
|
5
5
|
from slixmpp import JID
|
6
6
|
from slixmpp.jid import _unescape_node
|
@@ -9,6 +9,7 @@ from ..contact.roster import ESCAPE_TABLE
|
|
9
9
|
from ..core.mixins.lock import NamedLockMixin
|
10
10
|
from ..util import SubclassableOnce
|
11
11
|
from ..util.types import LegacyGroupIdType, LegacyMUCType
|
12
|
+
from .archive import MessageArchive
|
12
13
|
from .room import LegacyMUC
|
13
14
|
|
14
15
|
if TYPE_CHECKING:
|
@@ -27,17 +28,15 @@ class LegacyBookmarks(
|
|
27
28
|
def __init__(self, session: "BaseSession"):
|
28
29
|
self.session = session
|
29
30
|
self.xmpp = session.xmpp
|
30
|
-
self.
|
31
|
-
|
32
|
-
self._mucs_by_legacy_id = dict[LegacyGroupIdType, LegacyMUCType]()
|
33
|
-
self._mucs_by_bare_jid = dict[str, LegacyMUCType]()
|
31
|
+
self.user_jid = session.user_jid
|
32
|
+
self.__store = self.xmpp.store.rooms
|
34
33
|
|
35
34
|
self._muc_class: Type[LegacyMUCType] = LegacyMUC.get_self_or_unique_subclass()
|
36
35
|
|
37
|
-
self._user_nick: str = self.session.
|
36
|
+
self._user_nick: str = self.session.user_jid.node
|
38
37
|
|
39
38
|
super().__init__()
|
40
|
-
self.log = logging.getLogger(f"{self.
|
39
|
+
self.log = logging.getLogger(f"{self.user_jid.bare}:bookmarks")
|
41
40
|
self.ready = self.session.xmpp.loop.create_future()
|
42
41
|
if not self.xmpp.GROUPS:
|
43
42
|
self.ready.set_result(True)
|
@@ -50,20 +49,23 @@ class LegacyBookmarks(
|
|
50
49
|
def user_nick(self, nick: str):
|
51
50
|
self._user_nick = nick
|
52
51
|
|
53
|
-
def __iter__(self):
|
54
|
-
|
52
|
+
def __iter__(self) -> Iterator[LegacyMUCType]:
|
53
|
+
for stored in self.__store.get_all(user_pk=self.session.user_pk):
|
54
|
+
yield self._muc_class.from_store(self.session, stored)
|
55
55
|
|
56
56
|
def __repr__(self):
|
57
|
-
return f"<Bookmarks of {self.
|
57
|
+
return f"<Bookmarks of {self.user_jid}>"
|
58
58
|
|
59
59
|
async def __finish_init_muc(self, legacy_id: LegacyGroupIdType, jid: JID):
|
60
60
|
muc = self._muc_class(self.session, legacy_id=legacy_id, jid=jid)
|
61
|
-
|
62
|
-
|
63
|
-
muc.
|
64
|
-
|
65
|
-
|
66
|
-
|
61
|
+
with self.__store.session():
|
62
|
+
muc.pk = self.__store.add(self.session.user_pk, str(muc.legacy_id), muc.jid)
|
63
|
+
muc.archive = MessageArchive(muc.pk, self.xmpp.store.mam)
|
64
|
+
await muc.avatar_wrap_update_info()
|
65
|
+
if not muc.user_nick:
|
66
|
+
muc.user_nick = self._user_nick
|
67
|
+
self.log.debug("MUC created: %r", muc)
|
68
|
+
self.__store.update(muc)
|
67
69
|
return muc
|
68
70
|
|
69
71
|
async def legacy_id_to_jid_local_part(self, legacy_id: LegacyGroupIdType):
|
@@ -94,36 +96,50 @@ class LegacyBookmarks(
|
|
94
96
|
return _unescape_node(username)
|
95
97
|
|
96
98
|
async def by_jid(self, jid: JID) -> LegacyMUCType:
|
99
|
+
if jid.resource:
|
100
|
+
jid = JID(jid.bare)
|
97
101
|
bare = jid.bare
|
98
102
|
async with self.lock(("bare", bare)):
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
103
|
+
assert isinstance(jid.username, str)
|
104
|
+
legacy_id = await self.jid_local_part_to_legacy_id(jid.username)
|
105
|
+
if self.get_lock(("legacy_id", legacy_id)):
|
106
|
+
self.log.debug("Not instantiating %s after all", jid)
|
107
|
+
return await self.by_legacy_id(legacy_id)
|
108
|
+
|
109
|
+
with self.__store.session():
|
110
|
+
stored = self.__store.get_by_jid(self.session.user_pk, jid)
|
111
|
+
if stored is not None and stored.updated:
|
112
|
+
return self._muc_class.from_store(self.session, stored)
|
113
|
+
|
114
|
+
self.log.debug("Attempting to instantiate a new MUC for JID %s", jid)
|
115
|
+
local_part = jid.node
|
116
|
+
|
117
|
+
self.log.debug("%r is group %r", local_part, legacy_id)
|
118
|
+
return await self.__finish_init_muc(legacy_id, JID(bare))
|
119
|
+
|
120
|
+
def by_jid_only_if_exists(self, jid: JID) -> Optional[LegacyMUCType]:
|
121
|
+
with self.__store.session():
|
122
|
+
stored = self.__store.get_by_jid(self.session.user_pk, jid)
|
123
|
+
if stored is not None and stored.updated:
|
124
|
+
return self._muc_class.from_store(self.session, stored)
|
125
|
+
return None
|
112
126
|
|
113
127
|
async def by_legacy_id(self, legacy_id: LegacyGroupIdType) -> LegacyMUCType:
|
114
128
|
async with self.lock(("legacy_id", legacy_id)):
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
self.log.
|
129
|
+
with self.__store.session():
|
130
|
+
stored = self.__store.get_by_legacy_id(
|
131
|
+
self.session.user_pk, str(legacy_id)
|
132
|
+
)
|
133
|
+
if stored is not None and stored.updated:
|
134
|
+
return self._muc_class.from_store(self.session, stored)
|
135
|
+
self.log.debug("Create new MUC instance for legacy ID %s", legacy_id)
|
136
|
+
local = await self.legacy_id_to_jid_local_part(legacy_id)
|
137
|
+
bare = f"{local}@{self.xmpp.boundjid}"
|
138
|
+
jid = JID(bare)
|
139
|
+
if self.get_lock(("bare", bare)):
|
140
|
+
self.log.debug("Not instantiating %s after all", legacy_id)
|
141
|
+
return await self.by_jid(jid)
|
142
|
+
muc = await self.__finish_init_muc(legacy_id, jid)
|
127
143
|
|
128
144
|
return muc
|
129
145
|
|
@@ -146,18 +162,5 @@ class LegacyBookmarks(
|
|
146
162
|
)
|
147
163
|
|
148
164
|
def remove(self, muc: LegacyMUC):
|
149
|
-
|
150
|
-
|
151
|
-
except KeyError:
|
152
|
-
self.log.warning("Removed a MUC that we didn't store by legacy ID")
|
153
|
-
try:
|
154
|
-
del self._mucs_by_bare_jid[muc.jid.bare]
|
155
|
-
except KeyError:
|
156
|
-
self.log.warning("Removed a MUC that we didn't store by JID")
|
157
|
-
for part in muc._participants_by_contacts.values():
|
158
|
-
try:
|
159
|
-
part.contact.participants.remove(part)
|
160
|
-
except KeyError:
|
161
|
-
part.log.warning(
|
162
|
-
"That participant wasn't stored in the contact's participants attribute"
|
163
|
-
)
|
165
|
+
assert muc.pk is not None
|
166
|
+
self.__store.delete(muc.pk)
|
slidge/group/participant.py
CHANGED
@@ -6,7 +6,7 @@ import warnings
|
|
6
6
|
from copy import copy
|
7
7
|
from datetime import datetime
|
8
8
|
from functools import cached_property
|
9
|
-
from typing import TYPE_CHECKING, Optional, Union
|
9
|
+
from typing import TYPE_CHECKING, Optional, Self, Union
|
10
10
|
|
11
11
|
from slixmpp import JID, InvalidJID, Message, Presence
|
12
12
|
from slixmpp.plugins.xep_0045.stanza import MUCAdminItem
|
@@ -15,10 +15,16 @@ from slixmpp.types import MessageTypes, OptJid
|
|
15
15
|
from slixmpp.util.stringprep_profiles import StringPrepError, prohibit_output
|
16
16
|
|
17
17
|
from ..contact import LegacyContact
|
18
|
-
from ..core.mixins import
|
18
|
+
from ..core.mixins import (
|
19
|
+
ChatterDiscoMixin,
|
20
|
+
MessageMixin,
|
21
|
+
PresenceMixin,
|
22
|
+
StoredAttributeMixin,
|
23
|
+
)
|
24
|
+
from ..db.models import Participant
|
19
25
|
from ..util import SubclassableOnce, strip_illegal_chars
|
20
|
-
from ..util.sql import CachedPresence
|
21
26
|
from ..util.types import (
|
27
|
+
CachedPresence,
|
22
28
|
Hat,
|
23
29
|
LegacyMessageType,
|
24
30
|
MessageOrPresenceTypeVar,
|
@@ -40,6 +46,7 @@ def strip_non_printable(nickname: str):
|
|
40
46
|
|
41
47
|
|
42
48
|
class LegacyParticipant(
|
49
|
+
StoredAttributeMixin,
|
43
50
|
PresenceMixin,
|
44
51
|
MessageMixin,
|
45
52
|
ChatterDiscoMixin,
|
@@ -53,6 +60,7 @@ class LegacyParticipant(
|
|
53
60
|
_can_send_carbon = False
|
54
61
|
USE_STANZA_ID = True
|
55
62
|
STRIP_SHORT_DELAY = False
|
63
|
+
pk: int
|
56
64
|
|
57
65
|
def __init__(
|
58
66
|
self,
|
@@ -63,12 +71,11 @@ class LegacyParticipant(
|
|
63
71
|
role: MucRole = "participant",
|
64
72
|
affiliation: MucAffiliation = "member",
|
65
73
|
):
|
74
|
+
self.session = session = muc.session
|
75
|
+
self.xmpp = session.xmpp
|
66
76
|
super().__init__()
|
67
77
|
self._hats = list[Hat]()
|
68
78
|
self.muc = muc
|
69
|
-
self.session = session = muc.session
|
70
|
-
self.user = session.user
|
71
|
-
self.xmpp = session.xmpp
|
72
79
|
self._role = role
|
73
80
|
self._affiliation = affiliation
|
74
81
|
self.is_user: bool = is_user
|
@@ -84,8 +91,19 @@ class LegacyParticipant(
|
|
84
91
|
# if we didn't, we send it before the first message.
|
85
92
|
# this way, event in plugins that don't map "user has joined" events,
|
86
93
|
# we send a "join"-presence from the participant before the first message
|
87
|
-
self.
|
88
|
-
self.log = logging.getLogger(f"{self.
|
94
|
+
self._presence_sent: bool = False
|
95
|
+
self.log = logging.getLogger(f"{self.user_jid.bare}:{self.jid}")
|
96
|
+
self.__part_store = self.xmpp.store.participants
|
97
|
+
|
98
|
+
@property
|
99
|
+
def contact_pk(self) -> Optional[int]: # type:ignore
|
100
|
+
if self.contact:
|
101
|
+
return self.contact.contact_pk
|
102
|
+
return None
|
103
|
+
|
104
|
+
@property
|
105
|
+
def user_jid(self):
|
106
|
+
return self.session.user_jid
|
89
107
|
|
90
108
|
def __repr__(self):
|
91
109
|
return f"<Participant '{self.nickname}'/'{self.jid}' of '{self.muc}'>"
|
@@ -99,7 +117,8 @@ class LegacyParticipant(
|
|
99
117
|
if self._affiliation == affiliation:
|
100
118
|
return
|
101
119
|
self._affiliation = affiliation
|
102
|
-
|
120
|
+
self.__part_store.set_affiliation(self.pk, affiliation)
|
121
|
+
if not self._presence_sent:
|
103
122
|
return
|
104
123
|
self.send_last_presence(force=True, no_cache_online=True)
|
105
124
|
|
@@ -126,7 +145,8 @@ class LegacyParticipant(
|
|
126
145
|
if self._role == role:
|
127
146
|
return
|
128
147
|
self._role = role
|
129
|
-
|
148
|
+
self.__part_store.set_role(self.pk, role)
|
149
|
+
if not self._presence_sent:
|
130
150
|
return
|
131
151
|
self.send_last_presence(force=True, no_cache_online=True)
|
132
152
|
|
@@ -134,7 +154,8 @@ class LegacyParticipant(
|
|
134
154
|
if self._hats == hats:
|
135
155
|
return
|
136
156
|
self._hats = hats
|
137
|
-
|
157
|
+
self.__part_store.set_hats(self.pk, hats)
|
158
|
+
if not self._presence_sent:
|
138
159
|
return
|
139
160
|
self.send_last_presence(force=True, no_cache_online=True)
|
140
161
|
|
@@ -171,9 +192,6 @@ class LegacyParticipant(
|
|
171
192
|
except InvalidJID:
|
172
193
|
j.resource = strip_non_printable(nickname)
|
173
194
|
|
174
|
-
if nickname != unescaped_nickname:
|
175
|
-
self.muc._participants_by_escaped_nicknames[nickname] = self # type:ignore
|
176
|
-
|
177
195
|
self.jid = j
|
178
196
|
|
179
197
|
def send_configuration_change(self, codes: tuple[int]):
|
@@ -216,9 +234,6 @@ class LegacyParticipant(
|
|
216
234
|
p = self._make_presence(ptype="available", last_seen=last_seen, **kwargs)
|
217
235
|
self._send(p)
|
218
236
|
|
219
|
-
if old:
|
220
|
-
self.muc.rename_participant(old, new_nickname)
|
221
|
-
|
222
237
|
def _make_presence(
|
223
238
|
self,
|
224
239
|
*,
|
@@ -240,14 +255,14 @@ class LegacyParticipant(
|
|
240
255
|
if user_full_jid:
|
241
256
|
p["muc"]["jid"] = user_full_jid
|
242
257
|
else:
|
243
|
-
jid = copy(self.
|
258
|
+
jid = copy(self.user_jid)
|
244
259
|
try:
|
245
260
|
jid.resource = next(
|
246
|
-
iter(self.muc.
|
261
|
+
iter(self.muc.get_user_resources()) # type:ignore
|
247
262
|
)
|
248
263
|
except StopIteration:
|
249
264
|
jid.resource = "pseudo-resource"
|
250
|
-
p["muc"]["jid"] = self.
|
265
|
+
p["muc"]["jid"] = self.user_jid
|
251
266
|
codes.add(100)
|
252
267
|
elif self.contact:
|
253
268
|
p["muc"]["jid"] = self.contact.jid
|
@@ -257,7 +272,7 @@ class LegacyParticipant(
|
|
257
272
|
warnings.warn(
|
258
273
|
f"Private group but no 1:1 JID associated to '{self}'",
|
259
274
|
)
|
260
|
-
if self.is_user and (hash_ := self.session.avatar_hash):
|
275
|
+
if self.is_user and (hash_ := self.session.user.avatar_hash):
|
261
276
|
p["vcard_temp_update"]["photo"] = hash_
|
262
277
|
p["muc"]["status_codes"] = codes
|
263
278
|
return p
|
@@ -273,7 +288,7 @@ class LegacyParticipant(
|
|
273
288
|
archive_only
|
274
289
|
or self.is_system
|
275
290
|
or self.is_user
|
276
|
-
or self.
|
291
|
+
or self._presence_sent
|
277
292
|
or stanza["subject"]
|
278
293
|
):
|
279
294
|
return
|
@@ -303,9 +318,10 @@ class LegacyParticipant(
|
|
303
318
|
stanza["occupant-id"]["id"] = self.__occupant_id
|
304
319
|
self.__add_nick_element(stanza)
|
305
320
|
if isinstance(stanza, Presence):
|
306
|
-
if stanza["type"] == "unavailable" and not self.
|
321
|
+
if stanza["type"] == "unavailable" and not self._presence_sent:
|
307
322
|
return stanza # type:ignore
|
308
|
-
self.
|
323
|
+
self._presence_sent = True
|
324
|
+
self.__part_store.set_presence_sent(self.pk)
|
309
325
|
if full_jid:
|
310
326
|
stanza["to"] = full_jid
|
311
327
|
self.__send_presence_if_needed(stanza, full_jid, archive_only)
|
@@ -333,7 +349,7 @@ class LegacyParticipant(
|
|
333
349
|
item["role"] = self.role
|
334
350
|
if not self.muc.is_anonymous:
|
335
351
|
if self.is_user:
|
336
|
-
item["jid"] = self.
|
352
|
+
item["jid"] = self.user_jid.bare
|
337
353
|
elif self.contact:
|
338
354
|
item["jid"] = self.contact.jid.bare
|
339
355
|
else:
|
@@ -454,5 +470,41 @@ class LegacyParticipant(
|
|
454
470
|
msg["subject"] = subject or str(self.muc.name)
|
455
471
|
self._send(msg, full_jid)
|
456
472
|
|
473
|
+
@classmethod
|
474
|
+
def from_store(
|
475
|
+
cls,
|
476
|
+
session,
|
477
|
+
stored: Participant,
|
478
|
+
contact: Optional[LegacyContact] = None,
|
479
|
+
muc: Optional["LegacyMUC"] = None,
|
480
|
+
) -> Self:
|
481
|
+
from slidge.group.room import LegacyMUC
|
482
|
+
|
483
|
+
if muc is None:
|
484
|
+
muc = LegacyMUC.get_self_or_unique_subclass().from_store(
|
485
|
+
session, stored.room
|
486
|
+
)
|
487
|
+
part = cls(
|
488
|
+
muc,
|
489
|
+
stored.nickname,
|
490
|
+
role=stored.role,
|
491
|
+
affiliation=stored.affiliation,
|
492
|
+
)
|
493
|
+
part.pk = stored.id
|
494
|
+
if contact is not None:
|
495
|
+
part.contact = contact
|
496
|
+
elif stored.contact is not None:
|
497
|
+
contact = LegacyContact.get_self_or_unique_subclass().from_store(
|
498
|
+
session, stored.contact
|
499
|
+
)
|
500
|
+
part.contact = contact
|
501
|
+
|
502
|
+
part.is_user = stored.is_user
|
503
|
+
if (data := stored.extra_attributes) is not None:
|
504
|
+
muc.deserialize_extra_attributes(data)
|
505
|
+
part._presence_sent = stored.presence_sent
|
506
|
+
part._hats = [Hat(h.uri, h.title) for h in stored.hats]
|
507
|
+
return part
|
508
|
+
|
457
509
|
|
458
510
|
log = logging.getLogger(__name__)
|