slidge 0.1.3__py3-none-any.whl → 0.2.0a1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- slidge/__init__.py +3 -5
- slidge/__main__.py +2 -196
- slidge/__version__.py +5 -0
- slidge/command/adhoc.py +8 -1
- slidge/command/admin.py +6 -7
- slidge/command/base.py +1 -2
- slidge/command/register.py +32 -16
- slidge/command/user.py +85 -6
- slidge/contact/contact.py +165 -49
- slidge/contact/roster.py +122 -47
- slidge/core/config.py +14 -11
- slidge/core/gateway/base.py +148 -36
- slidge/core/gateway/caps.py +7 -5
- slidge/core/gateway/disco.py +2 -4
- slidge/core/gateway/mam.py +1 -4
- slidge/core/gateway/muc_admin.py +1 -1
- 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 +120 -57
- slidge/core/gateway/vcard_temp.py +7 -5
- slidge/core/mixins/__init__.py +11 -1
- slidge/core/mixins/attachment.py +32 -14
- slidge/core/mixins/avatar.py +90 -25
- slidge/core/mixins/base.py +8 -2
- slidge/core/mixins/db.py +18 -0
- slidge/core/mixins/disco.py +0 -10
- slidge/core/mixins/message.py +18 -8
- slidge/core/mixins/message_maker.py +17 -9
- slidge/core/mixins/presence.py +17 -4
- slidge/core/pubsub.py +54 -220
- slidge/core/session.py +69 -34
- 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/2461390c0af2_store_contacts_caps_verstring_in_db.py +36 -0
- slidge/db/alembic/versions/29f5280c61aa_store_subject_setter_in_room.py +37 -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/8d2ced764698_rely_on_db_to_store_contacts_rooms_and_.py +133 -0
- slidge/db/alembic/versions/aa9d82a7f6ef_db_creation.py +85 -0
- slidge/db/alembic/versions/b33993e87db3_move_everything_to_persistent_db.py +214 -0
- 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/alembic/versions/e91195719c2c_store_users_avatars_persistently.py +26 -0
- slidge/db/avatar.py +235 -0
- slidge/db/meta.py +65 -0
- slidge/db/models.py +375 -0
- slidge/db/store.py +1078 -0
- slidge/group/archive.py +58 -14
- slidge/group/bookmarks.py +72 -57
- slidge/group/participant.py +87 -28
- slidge/group/room.py +369 -211
- 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 +3 -0
- slidge/util/archive_msg.py +2 -1
- slidge/util/db.py +1 -47
- slidge/util/test.py +71 -4
- slidge/util/types.py +29 -4
- slidge/util/util.py +22 -0
- {slidge-0.1.3.dist-info → slidge-0.2.0a1.dist-info}/METADATA +4 -4
- slidge-0.2.0a1.dist-info/RECORD +114 -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.0a1.dist-info}/LICENSE +0 -0
- {slidge-0.1.3.dist-info → slidge-0.2.0a1.dist-info}/WHEEL +0 -0
- {slidge-0.1.3.dist-info → slidge-0.2.0a1.dist-info}/entry_points.txt +0 -0
slidge/group/archive.py
CHANGED
@@ -1,35 +1,39 @@
|
|
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.models import ArchivedMessage, ArchivedMessageSource
|
10
|
+
from ..db.store import MAMStore
|
9
11
|
from ..util.archive_msg import HistoryMessage
|
10
|
-
from ..util.
|
11
|
-
from ..util.sql import db
|
12
|
+
from ..util.types import HoleBound
|
12
13
|
|
13
14
|
if TYPE_CHECKING:
|
14
15
|
from .participant import LegacyParticipant
|
15
16
|
|
16
17
|
|
17
18
|
class MessageArchive:
|
18
|
-
def __init__(self,
|
19
|
-
self.
|
20
|
-
self.
|
21
|
-
db.mam_add_muc(db_id, user)
|
19
|
+
def __init__(self, room_pk: int, store: MAMStore):
|
20
|
+
self.room_pk = room_pk
|
21
|
+
self.__store = store
|
22
22
|
|
23
23
|
def add(
|
24
24
|
self,
|
25
25
|
msg: Message,
|
26
26
|
participant: Optional["LegacyParticipant"] = None,
|
27
|
+
archive_only=False,
|
28
|
+
legacy_msg_id=None,
|
27
29
|
):
|
28
30
|
"""
|
29
31
|
Add a message to the archive if it is deemed archivable
|
30
32
|
|
31
33
|
:param msg:
|
32
34
|
:param participant:
|
35
|
+
:param archive_only:
|
36
|
+
:param legacy_msg_id:
|
33
37
|
"""
|
34
38
|
if not archivable(msg):
|
35
39
|
return
|
@@ -40,7 +44,7 @@ class MessageArchive:
|
|
40
44
|
if participant.contact:
|
41
45
|
new_msg["muc"]["jid"] = participant.contact.jid.bare
|
42
46
|
elif participant.is_user:
|
43
|
-
new_msg["muc"]["jid"] = participant.
|
47
|
+
new_msg["muc"]["jid"] = participant.user_jid.bare
|
44
48
|
elif participant.is_system:
|
45
49
|
new_msg["muc"]["jid"] = participant.muc.jid
|
46
50
|
else:
|
@@ -49,11 +53,50 @@ class MessageArchive:
|
|
49
53
|
"jid"
|
50
54
|
] = f"{uuid.uuid4()}@{participant.xmpp.boundjid.bare}"
|
51
55
|
|
52
|
-
|
56
|
+
self.__store.add_message(
|
57
|
+
self.room_pk,
|
58
|
+
HistoryMessage(new_msg),
|
59
|
+
archive_only,
|
60
|
+
None if legacy_msg_id is None else str(legacy_msg_id),
|
61
|
+
)
|
53
62
|
|
54
63
|
def __iter__(self):
|
55
64
|
return iter(self.get_all())
|
56
65
|
|
66
|
+
@staticmethod
|
67
|
+
def __to_bound(stored: ArchivedMessage):
|
68
|
+
return HoleBound(
|
69
|
+
stored.legacy_id, # type:ignore
|
70
|
+
stored.timestamp.replace(tzinfo=timezone.utc),
|
71
|
+
)
|
72
|
+
|
73
|
+
def get_hole_bounds(self) -> tuple[HoleBound | None, HoleBound | None]:
|
74
|
+
most_recent = self.__store.get_most_recent_with_legacy_id(self.room_pk)
|
75
|
+
if most_recent is None:
|
76
|
+
return None, None
|
77
|
+
if most_recent.source == ArchivedMessageSource.BACKFILL:
|
78
|
+
# most recent = only backfill, fetch everything since last backfill
|
79
|
+
return self.__to_bound(most_recent), None
|
80
|
+
|
81
|
+
most_recent_back_filled = self.__store.get_most_recent_with_legacy_id(
|
82
|
+
self.room_pk, ArchivedMessageSource.BACKFILL
|
83
|
+
)
|
84
|
+
if most_recent_back_filled is None:
|
85
|
+
# group was never back-filled, fetch everything before first live
|
86
|
+
least_recent_live = self.__store.get_first(self.room_pk, True)
|
87
|
+
assert least_recent_live is not None
|
88
|
+
return None, self.__to_bound(least_recent_live)
|
89
|
+
|
90
|
+
assert most_recent_back_filled.legacy_id is not None
|
91
|
+
least_recent_live = self.__store.get_least_recent_with_legacy_id_after(
|
92
|
+
self.room_pk, most_recent_back_filled.legacy_id
|
93
|
+
)
|
94
|
+
assert least_recent_live is not None
|
95
|
+
# this is a hole caused by slidge downtime
|
96
|
+
return self.__to_bound(most_recent_back_filled), self.__to_bound(
|
97
|
+
least_recent_live
|
98
|
+
)
|
99
|
+
|
57
100
|
def get_all(
|
58
101
|
self,
|
59
102
|
start_date: Optional[datetime] = None,
|
@@ -65,9 +108,8 @@ class MessageArchive:
|
|
65
108
|
sender: Optional[str] = None,
|
66
109
|
flip=False,
|
67
110
|
):
|
68
|
-
for msg in
|
69
|
-
self.
|
70
|
-
self.db_id,
|
111
|
+
for msg in self.__store.get_messages(
|
112
|
+
self.room_pk,
|
71
113
|
before_id=before_id,
|
72
114
|
after_id=after_id,
|
73
115
|
ids=ids,
|
@@ -87,11 +129,13 @@ class MessageArchive:
|
|
87
129
|
:return:
|
88
130
|
"""
|
89
131
|
reply = iq.reply()
|
90
|
-
messages =
|
132
|
+
messages = self.__store.get_first_and_last(self.room_pk)
|
91
133
|
if messages:
|
92
134
|
for x, m in [("start", messages[0]), ("end", messages[-1])]:
|
93
135
|
reply["mam_metadata"][x]["id"] = m.id
|
94
|
-
reply["mam_metadata"][x]["timestamp"] = m.sent_on
|
136
|
+
reply["mam_metadata"][x]["timestamp"] = m.sent_on.replace(
|
137
|
+
tzinfo=timezone.utc
|
138
|
+
)
|
95
139
|
else:
|
96
140
|
reply.enable("mam_metadata")
|
97
141
|
reply.send()
|
slidge/group/bookmarks.py
CHANGED
@@ -1,14 +1,16 @@
|
|
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
|
+
from slixmpp.exceptions import XMPPError
|
6
7
|
from slixmpp.jid import _unescape_node
|
7
8
|
|
8
9
|
from ..contact.roster import ESCAPE_TABLE
|
9
10
|
from ..core.mixins.lock import NamedLockMixin
|
10
11
|
from ..util import SubclassableOnce
|
11
12
|
from ..util.types import LegacyGroupIdType, LegacyMUCType
|
13
|
+
from .archive import MessageArchive
|
12
14
|
from .room import LegacyMUC
|
13
15
|
|
14
16
|
if TYPE_CHECKING:
|
@@ -27,17 +29,15 @@ class LegacyBookmarks(
|
|
27
29
|
def __init__(self, session: "BaseSession"):
|
28
30
|
self.session = session
|
29
31
|
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]()
|
32
|
+
self.user_jid = session.user_jid
|
33
|
+
self.__store = self.xmpp.store.rooms
|
34
34
|
|
35
35
|
self._muc_class: Type[LegacyMUCType] = LegacyMUC.get_self_or_unique_subclass()
|
36
36
|
|
37
|
-
self._user_nick: str = self.session.
|
37
|
+
self._user_nick: str = self.session.user_jid.node
|
38
38
|
|
39
39
|
super().__init__()
|
40
|
-
self.log = logging.getLogger(f"{self.
|
40
|
+
self.log = logging.getLogger(f"{self.user_jid.bare}:bookmarks")
|
41
41
|
self.ready = self.session.xmpp.loop.create_future()
|
42
42
|
if not self.xmpp.GROUPS:
|
43
43
|
self.ready.set_result(True)
|
@@ -50,20 +50,34 @@ class LegacyBookmarks(
|
|
50
50
|
def user_nick(self, nick: str):
|
51
51
|
self._user_nick = nick
|
52
52
|
|
53
|
-
def __iter__(self):
|
54
|
-
|
53
|
+
def __iter__(self) -> Iterator[LegacyMUCType]:
|
54
|
+
for stored in self.__store.get_all(user_pk=self.session.user_pk):
|
55
|
+
yield self._muc_class.from_store(self.session, stored)
|
55
56
|
|
56
57
|
def __repr__(self):
|
57
|
-
return f"<Bookmarks of {self.
|
58
|
+
return f"<Bookmarks of {self.user_jid}>"
|
58
59
|
|
59
60
|
async def __finish_init_muc(self, legacy_id: LegacyGroupIdType, jid: JID):
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
61
|
+
with self.__store.session():
|
62
|
+
stored = self.__store.get_by_legacy_id(self.session.user_pk, str(legacy_id))
|
63
|
+
if stored is not None:
|
64
|
+
if stored.updated:
|
65
|
+
return self._muc_class.from_store(self.session, stored)
|
66
|
+
muc = self._muc_class(self.session, legacy_id=legacy_id, jid=jid)
|
67
|
+
muc.pk = stored.id
|
68
|
+
else:
|
69
|
+
muc = self._muc_class(self.session, legacy_id=legacy_id, jid=jid)
|
70
|
+
|
71
|
+
try:
|
72
|
+
with muc.updating_info():
|
73
|
+
await muc.avatar_wrap_update_info()
|
74
|
+
except Exception as e:
|
75
|
+
raise XMPPError("internal-server-error", str(e))
|
76
|
+
if not muc.user_nick:
|
77
|
+
muc.user_nick = self._user_nick
|
78
|
+
self.log.debug("MUC created: %r", muc)
|
79
|
+
muc.pk = self.__store.update(muc)
|
80
|
+
muc.archive = MessageArchive(muc.pk, self.xmpp.store.mam)
|
67
81
|
return muc
|
68
82
|
|
69
83
|
async def legacy_id_to_jid_local_part(self, legacy_id: LegacyGroupIdType):
|
@@ -94,36 +108,50 @@ class LegacyBookmarks(
|
|
94
108
|
return _unescape_node(username)
|
95
109
|
|
96
110
|
async def by_jid(self, jid: JID) -> LegacyMUCType:
|
111
|
+
if jid.resource:
|
112
|
+
jid = JID(jid.bare)
|
97
113
|
bare = jid.bare
|
98
114
|
async with self.lock(("bare", bare)):
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
115
|
+
assert isinstance(jid.username, str)
|
116
|
+
legacy_id = await self.jid_local_part_to_legacy_id(jid.username)
|
117
|
+
if self.get_lock(("legacy_id", legacy_id)):
|
118
|
+
self.log.debug("Not instantiating %s after all", jid)
|
119
|
+
return await self.by_legacy_id(legacy_id)
|
120
|
+
|
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
|
+
|
126
|
+
self.log.debug("Attempting to instantiate a new MUC for JID %s", jid)
|
127
|
+
local_part = jid.node
|
128
|
+
|
129
|
+
self.log.debug("%r is group %r", local_part, legacy_id)
|
130
|
+
return await self.__finish_init_muc(legacy_id, JID(bare))
|
131
|
+
|
132
|
+
def by_jid_only_if_exists(self, jid: JID) -> Optional[LegacyMUCType]:
|
133
|
+
with self.__store.session():
|
134
|
+
stored = self.__store.get_by_jid(self.session.user_pk, jid)
|
135
|
+
if stored is not None and stored.updated:
|
136
|
+
return self._muc_class.from_store(self.session, stored)
|
137
|
+
return None
|
112
138
|
|
113
139
|
async def by_legacy_id(self, legacy_id: LegacyGroupIdType) -> LegacyMUCType:
|
114
140
|
async with self.lock(("legacy_id", legacy_id)):
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
self.log.
|
141
|
+
with self.__store.session():
|
142
|
+
stored = self.__store.get_by_legacy_id(
|
143
|
+
self.session.user_pk, str(legacy_id)
|
144
|
+
)
|
145
|
+
if stored is not None and stored.updated:
|
146
|
+
return self._muc_class.from_store(self.session, stored)
|
147
|
+
self.log.debug("Create new MUC instance for legacy ID %s", legacy_id)
|
148
|
+
local = await self.legacy_id_to_jid_local_part(legacy_id)
|
149
|
+
bare = f"{local}@{self.xmpp.boundjid}"
|
150
|
+
jid = JID(bare)
|
151
|
+
if self.get_lock(("bare", bare)):
|
152
|
+
self.log.debug("Not instantiating %s after all", legacy_id)
|
153
|
+
return await self.by_jid(jid)
|
154
|
+
muc = await self.__finish_init_muc(legacy_id, jid)
|
127
155
|
|
128
156
|
return muc
|
129
157
|
|
@@ -146,18 +174,5 @@ class LegacyBookmarks(
|
|
146
174
|
)
|
147
175
|
|
148
176
|
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
|
-
)
|
177
|
+
assert muc.pk is not None
|
178
|
+
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,10 @@ class LegacyParticipant(
|
|
99
117
|
if self._affiliation == affiliation:
|
100
118
|
return
|
101
119
|
self._affiliation = affiliation
|
102
|
-
if not self.
|
120
|
+
if not self.muc._participants_filled:
|
121
|
+
return
|
122
|
+
self.__part_store.set_affiliation(self.pk, affiliation)
|
123
|
+
if not self._presence_sent:
|
103
124
|
return
|
104
125
|
self.send_last_presence(force=True, no_cache_online=True)
|
105
126
|
|
@@ -126,7 +147,10 @@ class LegacyParticipant(
|
|
126
147
|
if self._role == role:
|
127
148
|
return
|
128
149
|
self._role = role
|
129
|
-
if not self.
|
150
|
+
if not self.muc._participants_filled:
|
151
|
+
return
|
152
|
+
self.__part_store.set_role(self.pk, role)
|
153
|
+
if not self._presence_sent:
|
130
154
|
return
|
131
155
|
self.send_last_presence(force=True, no_cache_online=True)
|
132
156
|
|
@@ -134,7 +158,10 @@ class LegacyParticipant(
|
|
134
158
|
if self._hats == hats:
|
135
159
|
return
|
136
160
|
self._hats = hats
|
137
|
-
if not self.
|
161
|
+
if not self.muc._participants_filled:
|
162
|
+
return
|
163
|
+
self.__part_store.set_hats(self.pk, hats)
|
164
|
+
if not self._presence_sent:
|
138
165
|
return
|
139
166
|
self.send_last_presence(force=True, no_cache_online=True)
|
140
167
|
|
@@ -171,9 +198,6 @@ class LegacyParticipant(
|
|
171
198
|
except InvalidJID:
|
172
199
|
j.resource = strip_non_printable(nickname)
|
173
200
|
|
174
|
-
if nickname != unescaped_nickname:
|
175
|
-
self.muc._participants_by_escaped_nicknames[nickname] = self # type:ignore
|
176
|
-
|
177
201
|
self.jid = j
|
178
202
|
|
179
203
|
def send_configuration_change(self, codes: tuple[int]):
|
@@ -216,9 +240,6 @@ class LegacyParticipant(
|
|
216
240
|
p = self._make_presence(ptype="available", last_seen=last_seen, **kwargs)
|
217
241
|
self._send(p)
|
218
242
|
|
219
|
-
if old:
|
220
|
-
self.muc.rename_participant(old, new_nickname)
|
221
|
-
|
222
243
|
def _make_presence(
|
223
244
|
self,
|
224
245
|
*,
|
@@ -240,14 +261,14 @@ class LegacyParticipant(
|
|
240
261
|
if user_full_jid:
|
241
262
|
p["muc"]["jid"] = user_full_jid
|
242
263
|
else:
|
243
|
-
jid = copy(self.
|
264
|
+
jid = copy(self.user_jid)
|
244
265
|
try:
|
245
266
|
jid.resource = next(
|
246
|
-
iter(self.muc.
|
267
|
+
iter(self.muc.get_user_resources()) # type:ignore
|
247
268
|
)
|
248
269
|
except StopIteration:
|
249
270
|
jid.resource = "pseudo-resource"
|
250
|
-
p["muc"]["jid"] = self.
|
271
|
+
p["muc"]["jid"] = self.user_jid
|
251
272
|
codes.add(100)
|
252
273
|
elif self.contact:
|
253
274
|
p["muc"]["jid"] = self.contact.jid
|
@@ -257,7 +278,7 @@ class LegacyParticipant(
|
|
257
278
|
warnings.warn(
|
258
279
|
f"Private group but no 1:1 JID associated to '{self}'",
|
259
280
|
)
|
260
|
-
if self.is_user and (hash_ := self.session.avatar_hash):
|
281
|
+
if self.is_user and (hash_ := self.session.user.avatar_hash):
|
261
282
|
p["vcard_temp_update"]["photo"] = hash_
|
262
283
|
p["muc"]["status_codes"] = codes
|
263
284
|
return p
|
@@ -273,7 +294,7 @@ class LegacyParticipant(
|
|
273
294
|
archive_only
|
274
295
|
or self.is_system
|
275
296
|
or self.is_user
|
276
|
-
or self.
|
297
|
+
or self._presence_sent
|
277
298
|
or stanza["subject"]
|
278
299
|
):
|
279
300
|
return
|
@@ -298,14 +319,16 @@ class LegacyParticipant(
|
|
298
319
|
stanza: MessageOrPresenceTypeVar,
|
299
320
|
full_jid: Optional[JID] = None,
|
300
321
|
archive_only=False,
|
322
|
+
legacy_msg_id=None,
|
301
323
|
**send_kwargs,
|
302
324
|
) -> MessageOrPresenceTypeVar:
|
303
325
|
stanza["occupant-id"]["id"] = self.__occupant_id
|
304
326
|
self.__add_nick_element(stanza)
|
305
327
|
if isinstance(stanza, Presence):
|
306
|
-
if stanza["type"] == "unavailable" and not self.
|
328
|
+
if stanza["type"] == "unavailable" and not self._presence_sent:
|
307
329
|
return stanza # type:ignore
|
308
|
-
self.
|
330
|
+
self._presence_sent = True
|
331
|
+
self.__part_store.set_presence_sent(self.pk)
|
309
332
|
if full_jid:
|
310
333
|
stanza["to"] = full_jid
|
311
334
|
self.__send_presence_if_needed(stanza, full_jid, archive_only)
|
@@ -315,8 +338,8 @@ class LegacyParticipant(
|
|
315
338
|
else:
|
316
339
|
stanza.send()
|
317
340
|
else:
|
318
|
-
if isinstance(stanza, Message):
|
319
|
-
self.muc.archive.add(stanza, self)
|
341
|
+
if hasattr(self.muc, "archive") and isinstance(stanza, Message):
|
342
|
+
self.muc.archive.add(stanza, self, archive_only, legacy_msg_id)
|
320
343
|
if archive_only:
|
321
344
|
return stanza
|
322
345
|
for user_full_jid in self.muc.user_full_jids():
|
@@ -333,7 +356,7 @@ class LegacyParticipant(
|
|
333
356
|
item["role"] = self.role
|
334
357
|
if not self.muc.is_anonymous:
|
335
358
|
if self.is_user:
|
336
|
-
item["jid"] = self.
|
359
|
+
item["jid"] = self.user_jid.bare
|
337
360
|
elif self.contact:
|
338
361
|
item["jid"] = self.contact.jid.bare
|
339
362
|
else:
|
@@ -444,7 +467,7 @@ class LegacyParticipant(
|
|
444
467
|
):
|
445
468
|
if update_muc:
|
446
469
|
self.muc._subject = subject # type: ignore
|
447
|
-
self.muc.subject_setter = self
|
470
|
+
self.muc.subject_setter = self.nickname
|
448
471
|
self.muc.subject_date = when
|
449
472
|
|
450
473
|
msg = self._make_message()
|
@@ -454,5 +477,41 @@ class LegacyParticipant(
|
|
454
477
|
msg["subject"] = subject or str(self.muc.name)
|
455
478
|
self._send(msg, full_jid)
|
456
479
|
|
480
|
+
@classmethod
|
481
|
+
def from_store(
|
482
|
+
cls,
|
483
|
+
session,
|
484
|
+
stored: Participant,
|
485
|
+
contact: Optional[LegacyContact] = None,
|
486
|
+
muc: Optional["LegacyMUC"] = None,
|
487
|
+
) -> Self:
|
488
|
+
from slidge.group.room import LegacyMUC
|
489
|
+
|
490
|
+
if muc is None:
|
491
|
+
muc = LegacyMUC.get_self_or_unique_subclass().from_store(
|
492
|
+
session, stored.room
|
493
|
+
)
|
494
|
+
part = cls(
|
495
|
+
muc,
|
496
|
+
stored.nickname,
|
497
|
+
role=stored.role,
|
498
|
+
affiliation=stored.affiliation,
|
499
|
+
)
|
500
|
+
part.pk = stored.id
|
501
|
+
if contact is not None:
|
502
|
+
part.contact = contact
|
503
|
+
elif stored.contact is not None:
|
504
|
+
contact = LegacyContact.get_self_or_unique_subclass().from_store(
|
505
|
+
session, stored.contact
|
506
|
+
)
|
507
|
+
part.contact = contact
|
508
|
+
|
509
|
+
part.is_user = stored.is_user
|
510
|
+
if (data := stored.extra_attributes) is not None:
|
511
|
+
muc.deserialize_extra_attributes(data)
|
512
|
+
part._presence_sent = stored.presence_sent
|
513
|
+
part._hats = [Hat(h.uri, h.title) for h in stored.hats]
|
514
|
+
return part
|
515
|
+
|
457
516
|
|
458
517
|
log = logging.getLogger(__name__)
|