slidge 0.1.3__py3-none-any.whl → 0.2.0a1__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- slidge/__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__)
|