slidge 0.2.11__py3-none-any.whl → 0.3.0__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 +5 -2
- slidge/command/adhoc.py +9 -3
- slidge/command/admin.py +16 -12
- slidge/command/base.py +16 -12
- slidge/command/chat_command.py +25 -16
- slidge/command/user.py +7 -8
- slidge/contact/contact.py +123 -210
- slidge/contact/roster.py +108 -105
- slidge/core/config.py +2 -43
- slidge/core/dispatcher/caps.py +9 -2
- slidge/core/dispatcher/disco.py +13 -3
- slidge/core/dispatcher/message/__init__.py +1 -1
- slidge/core/dispatcher/message/chat_state.py +17 -8
- slidge/core/dispatcher/message/marker.py +7 -5
- slidge/core/dispatcher/message/message.py +120 -93
- slidge/core/dispatcher/muc/__init__.py +1 -1
- slidge/core/dispatcher/muc/admin.py +4 -4
- slidge/core/dispatcher/muc/mam.py +10 -6
- slidge/core/dispatcher/muc/misc.py +4 -2
- slidge/core/dispatcher/muc/owner.py +5 -3
- slidge/core/dispatcher/muc/ping.py +3 -1
- slidge/core/dispatcher/presence.py +26 -15
- slidge/core/dispatcher/registration.py +20 -12
- slidge/core/dispatcher/search.py +7 -3
- slidge/core/dispatcher/session_dispatcher.py +13 -5
- slidge/core/dispatcher/util.py +37 -27
- slidge/core/dispatcher/vcard.py +7 -4
- slidge/core/gateway.py +177 -87
- slidge/core/mixins/__init__.py +1 -11
- slidge/core/mixins/attachment.py +200 -147
- slidge/core/mixins/avatar.py +105 -177
- slidge/core/mixins/base.py +3 -1
- slidge/core/mixins/db.py +50 -2
- slidge/core/mixins/disco.py +1 -1
- slidge/core/mixins/message.py +19 -17
- slidge/core/mixins/message_maker.py +29 -15
- slidge/core/mixins/message_text.py +67 -30
- slidge/core/mixins/presence.py +94 -37
- slidge/core/pubsub.py +42 -47
- slidge/core/session.py +95 -60
- slidge/db/alembic/versions/cef02a8b1451_initial_schema.py +361 -0
- slidge/db/avatar.py +150 -119
- slidge/db/meta.py +33 -22
- slidge/db/models.py +69 -117
- slidge/db/store.py +414 -1094
- slidge/group/archive.py +65 -55
- slidge/group/bookmarks.py +96 -59
- slidge/group/participant.py +150 -144
- slidge/group/room.py +351 -328
- slidge/main.py +34 -22
- slidge/migration.py +17 -29
- slidge/slixfix/__init__.py +20 -4
- slidge/slixfix/delivery_receipt.py +6 -4
- slidge/slixfix/link_preview/link_preview.py +1 -1
- slidge/slixfix/link_preview/stanza.py +1 -1
- slidge/slixfix/roster.py +5 -7
- slidge/slixfix/xep_0077/register.py +8 -8
- slidge/slixfix/xep_0077/stanza.py +7 -7
- slidge/slixfix/xep_0100/gateway.py +12 -13
- slidge/slixfix/xep_0153/vcard_avatar.py +1 -1
- slidge/slixfix/xep_0292/vcard4.py +12 -2
- slidge/util/archive_msg.py +11 -5
- slidge/util/conf.py +27 -21
- slidge/util/jid_escaping.py +1 -1
- slidge/{core/mixins → util}/lock.py +6 -6
- slidge/util/test.py +30 -29
- slidge/util/types.py +24 -18
- slidge/util/util.py +26 -22
- {slidge-0.2.11.dist-info → slidge-0.3.0.dist-info}/METADATA +1 -1
- slidge-0.3.0.dist-info/RECORD +95 -0
- {slidge-0.2.11.dist-info → slidge-0.3.0.dist-info}/WHEEL +1 -1
- slidge/db/alembic/versions/04cf35e3cf85_add_participant_nickname_no_illegal.py +0 -33
- slidge/db/alembic/versions/09f27f098baa_add_missing_attributes_in_room.py +0 -36
- slidge/db/alembic/versions/15b0bd83407a_remove_bogus_unique_constraints_on_room_.py +0 -85
- slidge/db/alembic/versions/2461390c0af2_store_contacts_caps_verstring_in_db.py +0 -36
- slidge/db/alembic/versions/29f5280c61aa_store_subject_setter_in_room.py +0 -37
- slidge/db/alembic/versions/2b1f45ab7379_store_room_subject_setter_by_nickname.py +0 -41
- slidge/db/alembic/versions/3071e0fa69d4_add_contact_client_type.py +0 -52
- slidge/db/alembic/versions/45c24cc73c91_add_bob.py +0 -42
- slidge/db/alembic/versions/5bd48bfdffa2_lift_room_legacy_id_constraint.py +0 -61
- slidge/db/alembic/versions/82a4af84b679_add_muc_history_filled.py +0 -48
- slidge/db/alembic/versions/8b993243a536_add_vcard_content_to_contact_table.py +0 -43
- slidge/db/alembic/versions/8d2ced764698_rely_on_db_to_store_contacts_rooms_and_.py +0 -139
- slidge/db/alembic/versions/aa9d82a7f6ef_db_creation.py +0 -50
- slidge/db/alembic/versions/abba1ae0edb3_store_avatar_legacy_id_in_the_contact_.py +0 -79
- slidge/db/alembic/versions/b33993e87db3_move_everything_to_persistent_db.py +0 -214
- slidge/db/alembic/versions/b64b1a793483_add_source_and_legacy_id_for_archived_.py +0 -52
- slidge/db/alembic/versions/c4a8ec35a0e8_per_room_user_nick.py +0 -34
- slidge/db/alembic/versions/e91195719c2c_store_users_avatars_persistently.py +0 -26
- slidge-0.2.11.dist-info/RECORD +0 -112
- {slidge-0.2.11.dist-info → slidge-0.3.0.dist-info}/entry_points.txt +0 -0
- {slidge-0.2.11.dist-info → slidge-0.3.0.dist-info}/licenses/LICENSE +0 -0
- {slidge-0.2.11.dist-info → slidge-0.3.0.dist-info}/top_level.txt +0 -0
slidge/group/participant.py
CHANGED
@@ -5,19 +5,17 @@ import warnings
|
|
5
5
|
from copy import copy
|
6
6
|
from datetime import datetime
|
7
7
|
from functools import cached_property
|
8
|
-
from typing import TYPE_CHECKING,
|
8
|
+
from typing import TYPE_CHECKING, Any, Optional, Union
|
9
|
+
from xml.etree import ElementTree as ET
|
9
10
|
|
10
11
|
from slixmpp import JID, InvalidJID, Message, Presence
|
11
12
|
from slixmpp.plugins.xep_0045.stanza import MUCAdminItem
|
12
13
|
from slixmpp.types import MessageTypes, OptJid
|
14
|
+
from sqlalchemy.orm.exc import DetachedInstanceError
|
13
15
|
|
14
16
|
from ..contact import LegacyContact
|
15
|
-
from ..core.mixins import
|
16
|
-
|
17
|
-
MessageMixin,
|
18
|
-
PresenceMixin,
|
19
|
-
StoredAttributeMixin,
|
20
|
-
)
|
17
|
+
from ..core.mixins import ChatterDiscoMixin, MessageMixin, PresenceMixin
|
18
|
+
from ..core.mixins.db import DBMixin
|
21
19
|
from ..db.models import Participant
|
22
20
|
from ..util import SubclassableOnce, strip_illegal_chars
|
23
21
|
from ..util.types import (
|
@@ -43,97 +41,134 @@ def strip_non_printable(nickname: str):
|
|
43
41
|
|
44
42
|
|
45
43
|
class LegacyParticipant(
|
46
|
-
StoredAttributeMixin,
|
47
44
|
PresenceMixin,
|
48
45
|
MessageMixin,
|
49
46
|
ChatterDiscoMixin,
|
47
|
+
DBMixin,
|
50
48
|
metaclass=SubclassableOnce,
|
51
49
|
):
|
52
50
|
"""
|
53
51
|
A legacy participant of a legacy group chat.
|
54
52
|
"""
|
55
53
|
|
54
|
+
is_participant = True
|
55
|
+
|
56
56
|
mtype: MessageTypes = "groupchat"
|
57
57
|
_can_send_carbon = False
|
58
58
|
USE_STANZA_ID = True
|
59
59
|
STRIP_SHORT_DELAY = False
|
60
|
-
|
60
|
+
stored: Participant
|
61
|
+
contact: LegacyContact[Any] | None
|
61
62
|
|
62
63
|
def __init__(
|
63
64
|
self,
|
64
65
|
muc: "LegacyMUC",
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
affiliation: MucAffiliation = "member",
|
70
|
-
resource: str | None = None,
|
71
|
-
nickname_no_illegal: str | None = None,
|
72
|
-
):
|
73
|
-
self.session = session = muc.session
|
74
|
-
self.xmpp = session.xmpp
|
75
|
-
super().__init__()
|
76
|
-
self._hats = list[Hat]()
|
66
|
+
stored: Participant,
|
67
|
+
is_system: bool = False,
|
68
|
+
contact: LegacyContact[Any] | None = None,
|
69
|
+
) -> None:
|
77
70
|
self.muc = muc
|
78
|
-
self.
|
79
|
-
self.
|
80
|
-
self.
|
81
|
-
self.is_system: bool = is_system
|
71
|
+
self.session = muc.session
|
72
|
+
self.xmpp = muc.session.xmpp
|
73
|
+
self.is_system = is_system
|
82
74
|
|
83
|
-
|
75
|
+
if contact is None and stored.contact is not None:
|
76
|
+
contact = self.session.contacts.from_store(stored=stored.contact)
|
77
|
+
if contact is not None and stored.contact is None:
|
78
|
+
stored.contact = contact.stored
|
84
79
|
|
85
|
-
|
86
|
-
|
87
|
-
else:
|
88
|
-
assert nickname_no_illegal is not None
|
89
|
-
self._nickname_no_illegal = nickname_no_illegal
|
90
|
-
self.jid = JID(self.muc.jid)
|
91
|
-
self.jid.resource = resource
|
80
|
+
self.stored = stored
|
81
|
+
self.contact = contact
|
92
82
|
|
93
|
-
|
83
|
+
super().__init__()
|
84
|
+
|
85
|
+
if stored.resource is None:
|
86
|
+
self.__update_resource(stored.nickname)
|
94
87
|
|
95
|
-
self.contact: Optional["LegacyContact"] = None
|
96
|
-
# we track if we already sent a presence for this participant.
|
97
|
-
# if we didn't, we send it before the first message.
|
98
|
-
# this way, event in plugins that don't map "user has joined" events,
|
99
|
-
# we send a "join"-presence from the participant before the first message
|
100
|
-
self._presence_sent: bool = False
|
101
88
|
self.log = logging.getLogger(f"{self.user_jid.bare}:{self.jid}")
|
102
|
-
self.__part_store = self.xmpp.store.participants
|
103
89
|
|
104
90
|
@property
|
105
|
-
def
|
106
|
-
|
107
|
-
return self.
|
108
|
-
|
91
|
+
def is_user(self) -> bool:
|
92
|
+
try:
|
93
|
+
return self.stored.is_user
|
94
|
+
except DetachedInstanceError:
|
95
|
+
self.merge()
|
96
|
+
return self.stored.is_user
|
97
|
+
|
98
|
+
@is_user.setter
|
99
|
+
def is_user(self, is_user: bool) -> None:
|
100
|
+
with self.xmpp.store.session(expire_on_commit=True) as orm:
|
101
|
+
orm.add(self.stored)
|
102
|
+
self.stored.is_user = is_user
|
103
|
+
orm.commit()
|
104
|
+
|
105
|
+
@property
|
106
|
+
def jid(self) -> JID:
|
107
|
+
jid = JID(self.muc.jid)
|
108
|
+
if self.stored.resource:
|
109
|
+
jid.resource = self.stored.resource
|
110
|
+
return jid
|
111
|
+
|
112
|
+
@jid.setter
|
113
|
+
def jid(self, x: JID):
|
114
|
+
# FIXME: without this, mypy yields
|
115
|
+
# "Cannot override writeable attribute with read-only property"
|
116
|
+
# But it does not happen for LegacyContact. WTF?
|
117
|
+
raise RuntimeError
|
118
|
+
|
119
|
+
def commit(self, *args, **kwargs) -> None:
|
120
|
+
if self.is_system:
|
121
|
+
return
|
122
|
+
if self.muc.get_lock("fill participants") or self.muc.get_lock("fill history"):
|
123
|
+
return
|
124
|
+
super().commit(*args, **kwargs)
|
109
125
|
|
110
126
|
@property
|
111
127
|
def user_jid(self):
|
112
128
|
return self.session.user_jid
|
113
129
|
|
114
|
-
def __repr__(self):
|
130
|
+
def __repr__(self) -> str:
|
115
131
|
return f"<Participant '{self.nickname}'/'{self.jid}' of '{self.muc}'>"
|
116
132
|
|
133
|
+
@property
|
134
|
+
def _presence_sent(self) -> bool:
|
135
|
+
# we track if we already sent a presence for this participant.
|
136
|
+
# if we didn't, we send it before the first message.
|
137
|
+
# this way, event in plugins that don't map "user has joined" events,
|
138
|
+
# we send a "join"-presence from the participant before the first message
|
139
|
+
return self.stored.presence_sent
|
140
|
+
|
141
|
+
@_presence_sent.setter
|
142
|
+
def _presence_sent(self, val: bool) -> None:
|
143
|
+
if self._presence_sent == val:
|
144
|
+
return
|
145
|
+
self.stored.presence_sent = val
|
146
|
+
self.commit(merge=True)
|
147
|
+
|
148
|
+
@property
|
149
|
+
def nickname_no_illegal(self) -> str:
|
150
|
+
return self.stored.nickname_no_illegal
|
151
|
+
|
117
152
|
@property
|
118
153
|
def affiliation(self):
|
119
|
-
return self.
|
154
|
+
return self.stored.affiliation
|
120
155
|
|
121
156
|
@affiliation.setter
|
122
|
-
def affiliation(self, affiliation: MucAffiliation):
|
123
|
-
if self.
|
157
|
+
def affiliation(self, affiliation: MucAffiliation) -> None:
|
158
|
+
if self.affiliation == affiliation:
|
124
159
|
return
|
125
|
-
self.
|
126
|
-
if not self.muc.
|
160
|
+
self.stored.affiliation = affiliation
|
161
|
+
if not self.muc.participants_filled:
|
127
162
|
return
|
128
|
-
self.
|
163
|
+
self.commit()
|
129
164
|
if not self._presence_sent:
|
130
165
|
return
|
131
166
|
self.send_last_presence(force=True, no_cache_online=True)
|
132
167
|
|
133
|
-
def send_affiliation_change(self):
|
168
|
+
def send_affiliation_change(self) -> None:
|
134
169
|
# internal use by slidge
|
135
170
|
msg = self._make_message()
|
136
|
-
msg["muc"]["affiliation"] = self.
|
171
|
+
msg["muc"]["affiliation"] = self.affiliation
|
137
172
|
msg["type"] = "normal"
|
138
173
|
if not self.muc.is_anonymous and not self.is_system:
|
139
174
|
if self.contact:
|
@@ -146,48 +181,53 @@ class LegacyParticipant(
|
|
146
181
|
|
147
182
|
@property
|
148
183
|
def role(self):
|
149
|
-
return self.
|
184
|
+
return self.stored.role
|
150
185
|
|
151
186
|
@role.setter
|
152
|
-
def role(self, role: MucRole):
|
153
|
-
if self.
|
187
|
+
def role(self, role: MucRole) -> None:
|
188
|
+
if self.role == role:
|
154
189
|
return
|
155
|
-
self.
|
156
|
-
if not self.muc.
|
190
|
+
self.stored.role = role
|
191
|
+
if not self.muc.participants_filled:
|
157
192
|
return
|
158
|
-
self.
|
193
|
+
self.commit()
|
159
194
|
if not self._presence_sent:
|
160
195
|
return
|
161
196
|
self.send_last_presence(force=True, no_cache_online=True)
|
162
197
|
|
163
|
-
|
164
|
-
|
198
|
+
@property
|
199
|
+
def hats(self) -> list[Hat]:
|
200
|
+
return [Hat(*h) for h in self.stored.hats] if self.stored.hats else []
|
201
|
+
|
202
|
+
def set_hats(self, hats: list[Hat]) -> None:
|
203
|
+
if self.hats == hats:
|
165
204
|
return
|
166
|
-
self.
|
167
|
-
if not self.muc.
|
205
|
+
self.stored.hats = hats # type:ignore[assignment]
|
206
|
+
if not self.muc.participants_filled:
|
168
207
|
return
|
169
|
-
self.
|
208
|
+
self.commit(merge=True)
|
170
209
|
if not self._presence_sent:
|
171
210
|
return
|
172
211
|
self.send_last_presence(force=True, no_cache_online=True)
|
173
212
|
|
174
|
-
def
|
213
|
+
def __update_resource(self, unescaped_nickname: Optional[str]) -> None:
|
175
214
|
if not unescaped_nickname:
|
176
|
-
self.
|
215
|
+
self.stored.resource = ""
|
177
216
|
if self.is_system:
|
178
|
-
self.
|
217
|
+
self.stored.nickname_no_illegal = ""
|
179
218
|
else:
|
180
219
|
warnings.warn(
|
181
220
|
"Only the system participant is allowed to not have a nickname"
|
182
221
|
)
|
183
222
|
nickname = f"unnamed-{uuid.uuid4()}"
|
184
|
-
self.
|
223
|
+
self.stored.resource = self.stored.nickname_no_illegal = nickname
|
185
224
|
return
|
186
225
|
|
187
|
-
self.
|
226
|
+
self.stored.nickname_no_illegal, jid = escape_nickname(
|
188
227
|
self.muc.jid,
|
189
228
|
unescaped_nickname,
|
190
229
|
)
|
230
|
+
self.stored.resource = jid.resource
|
191
231
|
|
192
232
|
def send_configuration_change(self, codes: tuple[int]):
|
193
233
|
if not self.is_system:
|
@@ -198,11 +238,11 @@ class LegacyParticipant(
|
|
198
238
|
|
199
239
|
@property
|
200
240
|
def nickname(self):
|
201
|
-
return self.
|
241
|
+
return self.stored.nickname
|
202
242
|
|
203
243
|
@nickname.setter
|
204
|
-
def nickname(self, new_nickname: str):
|
205
|
-
old = self.
|
244
|
+
def nickname(self, new_nickname: str) -> None:
|
245
|
+
old = self.nickname
|
206
246
|
if new_nickname == old:
|
207
247
|
return
|
208
248
|
|
@@ -219,18 +259,16 @@ class LegacyParticipant(
|
|
219
259
|
p = self._make_presence(ptype="unavailable", last_seen=last_seen, **kwargs)
|
220
260
|
# in this order so pfrom=old resource and we actually use the escaped nick
|
221
261
|
# in the muc/item/nick element
|
222
|
-
self.
|
262
|
+
self.__update_resource(new_nickname)
|
223
263
|
p["muc"]["item"]["nick"] = self.jid.resource
|
224
264
|
self._send(p)
|
225
265
|
|
226
|
-
self.
|
227
|
-
|
266
|
+
self.stored.nickname = new_nickname
|
267
|
+
self.commit()
|
228
268
|
kwargs["status_codes"] = set()
|
229
269
|
p = self._make_presence(ptype="available", last_seen=last_seen, **kwargs)
|
230
270
|
self._send(p)
|
231
271
|
|
232
|
-
self.__part_store.update(self)
|
233
|
-
|
234
272
|
def _make_presence(
|
235
273
|
self,
|
236
274
|
*,
|
@@ -242,8 +280,8 @@ class LegacyParticipant(
|
|
242
280
|
p = super()._make_presence(last_seen=last_seen, **presence_kwargs)
|
243
281
|
p["muc"]["affiliation"] = self.affiliation
|
244
282
|
p["muc"]["role"] = self.role
|
245
|
-
if self.
|
246
|
-
p["hats"].add_hats(self.
|
283
|
+
if self.hats:
|
284
|
+
p["hats"].add_hats(self.hats)
|
247
285
|
codes = status_codes or set()
|
248
286
|
if self.is_user:
|
249
287
|
codes.add(110)
|
@@ -254,9 +292,7 @@ class LegacyParticipant(
|
|
254
292
|
else:
|
255
293
|
jid = JID(self.user_jid)
|
256
294
|
try:
|
257
|
-
jid.resource = next(
|
258
|
-
iter(self.muc.get_user_resources()) # type:ignore
|
259
|
-
)
|
295
|
+
jid.resource = next(iter(self.muc.get_user_resources()))
|
260
296
|
except StopIteration:
|
261
297
|
jid.resource = "pseudo-resource"
|
262
298
|
p["muc"]["jid"] = self.user_jid
|
@@ -275,12 +311,12 @@ class LegacyParticipant(
|
|
275
311
|
return p
|
276
312
|
|
277
313
|
@property
|
278
|
-
def DISCO_NAME(self):
|
314
|
+
def DISCO_NAME(self): # type:ignore[override]
|
279
315
|
return self.nickname
|
280
316
|
|
281
317
|
def __send_presence_if_needed(
|
282
318
|
self, stanza: Union[Message, Presence], full_jid: JID, archive_only: bool
|
283
|
-
):
|
319
|
+
) -> None:
|
284
320
|
if (
|
285
321
|
archive_only
|
286
322
|
or self.is_system
|
@@ -309,8 +345,9 @@ class LegacyParticipant(
|
|
309
345
|
self,
|
310
346
|
stanza: MessageOrPresenceTypeVar,
|
311
347
|
full_jid: Optional[JID] = None,
|
312
|
-
archive_only=False,
|
348
|
+
archive_only: bool = False,
|
313
349
|
legacy_msg_id=None,
|
350
|
+
initial_presence=False,
|
314
351
|
**send_kwargs,
|
315
352
|
) -> MessageOrPresenceTypeVar:
|
316
353
|
if stanza.get_from().resource:
|
@@ -321,8 +358,10 @@ class LegacyParticipant(
|
|
321
358
|
if not self.is_user and isinstance(stanza, Presence):
|
322
359
|
if stanza["type"] == "unavailable" and not self._presence_sent:
|
323
360
|
return stanza # type:ignore
|
324
|
-
|
325
|
-
|
361
|
+
if initial_presence:
|
362
|
+
self.stored.presence_sent = True
|
363
|
+
else:
|
364
|
+
self._presence_sent = True
|
326
365
|
if full_jid:
|
327
366
|
stanza["to"] = full_jid
|
328
367
|
self.__send_presence_if_needed(stanza, full_jid, archive_only)
|
@@ -362,8 +401,8 @@ class LegacyParticipant(
|
|
362
401
|
)
|
363
402
|
return item
|
364
403
|
|
365
|
-
def __add_nick_element(self, stanza: Union[Presence, Message]):
|
366
|
-
if (nick := self.
|
404
|
+
def __add_nick_element(self, stanza: Union[Presence, Message]) -> None:
|
405
|
+
if (nick := self.nickname_no_illegal) != self.jid.resource:
|
367
406
|
n = self.xmpp.plugin["xep_0172"].stanza.UserNick()
|
368
407
|
n["nick"] = nick
|
369
408
|
stanza.append(n)
|
@@ -377,9 +416,9 @@ class LegacyParticipant(
|
|
377
416
|
def send_initial_presence(
|
378
417
|
self,
|
379
418
|
full_jid: JID,
|
380
|
-
nick_change=False,
|
419
|
+
nick_change: bool = False,
|
381
420
|
presence_id: Optional[str] = None,
|
382
|
-
):
|
421
|
+
) -> None:
|
383
422
|
"""
|
384
423
|
Called when the user joins a MUC, as a mechanism
|
385
424
|
to indicate to the joining XMPP client the list of "participants".
|
@@ -418,21 +457,21 @@ class LegacyParticipant(
|
|
418
457
|
)
|
419
458
|
if presence_id:
|
420
459
|
p["id"] = presence_id
|
421
|
-
self._send(p, full_jid)
|
460
|
+
self._send(p, full_jid, initial_presence=True)
|
422
461
|
|
423
|
-
def leave(self):
|
462
|
+
def leave(self) -> None:
|
424
463
|
"""
|
425
464
|
Call this when the participant leaves the room
|
426
465
|
"""
|
427
466
|
self.muc.remove_participant(self)
|
428
467
|
|
429
|
-
def kick(self, reason: str | None = None):
|
468
|
+
def kick(self, reason: str | None = None) -> None:
|
430
469
|
"""
|
431
470
|
Call this when the participant is kicked from the room
|
432
471
|
"""
|
433
472
|
self.muc.remove_participant(self, kick=True, reason=reason)
|
434
473
|
|
435
|
-
def ban(self, reason: str | None = None):
|
474
|
+
def ban(self, reason: str | None = None) -> None:
|
436
475
|
"""
|
437
476
|
Call this when the participant is banned from the room
|
438
477
|
"""
|
@@ -443,15 +482,16 @@ class LegacyParticipant(
|
|
443
482
|
return self.contact.get_disco_info()
|
444
483
|
return super().get_disco_info()
|
445
484
|
|
446
|
-
def moderate(
|
485
|
+
def moderate(
|
486
|
+
self, legacy_msg_id: LegacyMessageType, reason: Optional[str] = None
|
487
|
+
) -> None:
|
447
488
|
xmpp_id = self._legacy_to_xmpp(legacy_msg_id)
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
msg_ids = multi + [xmpp_id]
|
489
|
+
with self.xmpp.store.session() as orm:
|
490
|
+
msg_ids = self.xmpp.store.id_map.get_xmpp(
|
491
|
+
orm, self.muc.stored.id, str(legacy_msg_id), True
|
492
|
+
)
|
453
493
|
|
454
|
-
for i in msg_ids:
|
494
|
+
for i in set(msg_ids + [xmpp_id]):
|
455
495
|
m = self.muc.get_system_participant()._make_message()
|
456
496
|
m["retract"]["id"] = i
|
457
497
|
if self.is_system:
|
@@ -468,8 +508,8 @@ class LegacyParticipant(
|
|
468
508
|
subject: str,
|
469
509
|
full_jid: Optional[JID] = None,
|
470
510
|
when: Optional[datetime] = None,
|
471
|
-
update_muc=True,
|
472
|
-
):
|
511
|
+
update_muc: bool = True,
|
512
|
+
) -> None:
|
473
513
|
if update_muc:
|
474
514
|
self.muc._subject = subject # type: ignore
|
475
515
|
self.muc.subject_setter = self.nickname
|
@@ -479,47 +519,13 @@ class LegacyParticipant(
|
|
479
519
|
if when is not None:
|
480
520
|
msg["delay"].set_stamp(when)
|
481
521
|
msg["delay"]["from"] = self.muc.jid
|
482
|
-
|
522
|
+
if subject:
|
523
|
+
msg["subject"] = subject
|
524
|
+
else:
|
525
|
+
# may be simplified if slixmpp lets it do it more easily some day
|
526
|
+
msg.xml.append(ET.Element(f"{{{msg.namespace}}}subject"))
|
483
527
|
self._send(msg, full_jid)
|
484
528
|
|
485
|
-
@classmethod
|
486
|
-
def from_store(
|
487
|
-
cls,
|
488
|
-
session,
|
489
|
-
stored: Participant,
|
490
|
-
contact: Optional[LegacyContact] = None,
|
491
|
-
muc: Optional["LegacyMUC"] = None,
|
492
|
-
) -> Self:
|
493
|
-
from slidge.group.room import LegacyMUC
|
494
|
-
|
495
|
-
if muc is None:
|
496
|
-
muc = LegacyMUC.get_self_or_unique_subclass().from_store(
|
497
|
-
session, stored.room
|
498
|
-
)
|
499
|
-
part = cls(
|
500
|
-
muc,
|
501
|
-
stored.nickname,
|
502
|
-
role=stored.role,
|
503
|
-
affiliation=stored.affiliation,
|
504
|
-
resource=stored.resource,
|
505
|
-
nickname_no_illegal=stored.nickname_no_illegal,
|
506
|
-
)
|
507
|
-
part.pk = stored.id
|
508
|
-
if contact is not None:
|
509
|
-
part.contact = contact
|
510
|
-
elif stored.contact is not None:
|
511
|
-
contact = LegacyContact.get_self_or_unique_subclass().from_store(
|
512
|
-
session, stored.contact
|
513
|
-
)
|
514
|
-
part.contact = contact
|
515
|
-
|
516
|
-
part.is_user = stored.is_user
|
517
|
-
if (data := stored.extra_attributes) is not None:
|
518
|
-
muc.deserialize_extra_attributes(data)
|
519
|
-
part._presence_sent = stored.presence_sent
|
520
|
-
part._hats = [Hat(h.uri, h.title) for h in stored.hats]
|
521
|
-
return part
|
522
|
-
|
523
529
|
|
524
530
|
def escape_nickname(muc_jid: JID, nickname: str) -> tuple[str, JID]:
|
525
531
|
nickname = nickname_no_illegal = strip_illegal_chars(nickname)
|