slidge 0.2.0a10__py3-none-any.whl → 0.2.0b0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- slidge/__main__.py +2 -3
- slidge/__version__.py +1 -1
- slidge/command/user.py +5 -1
- slidge/contact/roster.py +2 -0
- slidge/core/config.py +0 -3
- slidge/core/dispatcher/message/marker.py +2 -7
- slidge/core/dispatcher/muc/misc.py +3 -0
- slidge/core/dispatcher/muc/owner.py +1 -1
- slidge/core/dispatcher/util.py +23 -23
- slidge/core/mixins/attachment.py +24 -8
- slidge/core/mixins/lock.py +10 -8
- slidge/core/mixins/message.py +5 -205
- slidge/core/mixins/message_text.py +211 -0
- slidge/db/alembic/versions/15b0bd83407a_remove_bogus_unique_constraints_on_room_.py +83 -0
- slidge/db/alembic/versions/5bd48bfdffa2_lift_room_legacy_id_constraint.py +12 -1
- slidge/db/models.py +3 -1
- slidge/db/store.py +16 -9
- slidge/group/bookmarks.py +23 -1
- slidge/group/participant.py +5 -5
- slidge/group/room.py +10 -1
- slidge/util/test.py +4 -4
- {slidge-0.2.0a10.dist-info → slidge-0.2.0b0.dist-info}/METADATA +1 -1
- {slidge-0.2.0a10.dist-info → slidge-0.2.0b0.dist-info}/RECORD +26 -24
- slidge-0.2.0b0.dist-info/entry_points.txt +3 -0
- slidge-0.2.0a10.dist-info/entry_points.txt +0 -3
- {slidge-0.2.0a10.dist-info → slidge-0.2.0b0.dist-info}/LICENSE +0 -0
- {slidge-0.2.0a10.dist-info → slidge-0.2.0b0.dist-info}/WHEEL +0 -0
slidge/__main__.py
CHANGED
slidge/__version__.py
CHANGED
slidge/command/user.py
CHANGED
@@ -305,7 +305,10 @@ class LeaveGroup(Command):
|
|
305
305
|
FormField(
|
306
306
|
"group",
|
307
307
|
"Group name",
|
308
|
-
|
308
|
+
type="list-single",
|
309
|
+
options=[
|
310
|
+
{"label": g.name, "value": str(i)} for i, g in enumerate(groups)
|
311
|
+
],
|
309
312
|
)
|
310
313
|
],
|
311
314
|
handler=self.confirm, # type:ignore
|
@@ -329,3 +332,4 @@ class LeaveGroup(Command):
|
|
329
332
|
@staticmethod
|
330
333
|
async def finish(session: AnyBaseSession, _ifrom, group: LegacyMUC):
|
331
334
|
await session.on_leave_group(group.legacy_id)
|
335
|
+
await session.bookmarks.remove(group, reason="You left this group via slidge.")
|
slidge/contact/roster.py
CHANGED
@@ -154,6 +154,8 @@ class LegacyRoster(
|
|
154
154
|
try:
|
155
155
|
with contact.updating_info():
|
156
156
|
await contact.avatar_wrap_update_info()
|
157
|
+
except XMPPError:
|
158
|
+
raise
|
157
159
|
except Exception as e:
|
158
160
|
raise XMPPError("internal-server-error", str(e))
|
159
161
|
contact._caps_ver = await contact.get_caps_ver(contact.jid)
|
slidge/core/config.py
CHANGED
@@ -154,9 +154,6 @@ LAST_SEEN_FALLBACK__DOC = (
|
|
154
154
|
QR_TIMEOUT = 60
|
155
155
|
QR_TIMEOUT__DOC = "Timeout for QR code flashing confirmation."
|
156
156
|
|
157
|
-
DOWNLOAD_CHUNK_SIZE = 1024
|
158
|
-
DOWNLOAD_CHUNK_SIZE__DOC = "Chunk size when slidge needs to download files using HTTP."
|
159
|
-
|
160
157
|
LAST_MESSAGE_CORRECTION_RETRACTION_WORKAROUND = False
|
161
158
|
LAST_MESSAGE_CORRECTION_RETRACTION_WORKAROUND__DOC = (
|
162
159
|
"If the legacy service does not support last message correction but supports"
|
@@ -3,12 +3,7 @@ from slixmpp.xmlstream import StanzaBase
|
|
3
3
|
|
4
4
|
from ....group.room import LegacyMUC
|
5
5
|
from ....util.types import Recipient
|
6
|
-
from ..util import
|
7
|
-
DispatcherMixin,
|
8
|
-
_get_entity,
|
9
|
-
_xmpp_to_legacy_thread,
|
10
|
-
exceptions_to_xmpp_errors,
|
11
|
-
)
|
6
|
+
from ..util import DispatcherMixin, _get_entity, exceptions_to_xmpp_errors
|
12
7
|
|
13
8
|
|
14
9
|
class MarkerMixin(DispatcherMixin):
|
@@ -26,7 +21,7 @@ class MarkerMixin(DispatcherMixin):
|
|
26
21
|
session = await self._get_session(msg)
|
27
22
|
|
28
23
|
e: Recipient = await _get_entity(session, msg)
|
29
|
-
legacy_thread = await _xmpp_to_legacy_thread(session, msg, e)
|
24
|
+
legacy_thread = await self._xmpp_to_legacy_thread(session, msg, e)
|
30
25
|
displayed_msg_id = msg["displayed"]["id"]
|
31
26
|
if not isinstance(e, LegacyMUC) and self.xmpp.MARK_ALL_MESSAGES:
|
32
27
|
to_mark = e.get_msg_xmpp_id_up_to(displayed_msg_id) # type: ignore
|
@@ -54,6 +54,9 @@ class MucMiscMixin(DispatcherMixin):
|
|
54
54
|
muc = await self.get_muc_from_stanza(iq)
|
55
55
|
await muc.session.on_leave_group(muc.legacy_id)
|
56
56
|
iq.reply().send()
|
57
|
+
await muc.session.bookmarks.remove(
|
58
|
+
muc, "You left this chat from an XMPP client."
|
59
|
+
)
|
57
60
|
return
|
58
61
|
|
59
62
|
raise XMPPError("feature-not-implemented")
|
@@ -88,7 +88,7 @@ class MucOwnerMixin(DispatcherMixin):
|
|
88
88
|
if reason is not None:
|
89
89
|
presence["muc"]["destroy"]["reason"] = reason
|
90
90
|
user_participant._send(presence)
|
91
|
-
muc.session.bookmarks.remove(muc)
|
91
|
+
await muc.session.bookmarks.remove(muc, kick=False)
|
92
92
|
clear = True
|
93
93
|
else:
|
94
94
|
raise XMPPError("bad-request")
|
slidge/core/dispatcher/util.py
CHANGED
@@ -96,9 +96,31 @@ class DispatcherMixin:
|
|
96
96
|
) -> tuple["BaseSession", Recipient, int | str]:
|
97
97
|
session = await self._get_session(msg)
|
98
98
|
e: Recipient = await _get_entity(session, msg)
|
99
|
-
legacy_thread = await _xmpp_to_legacy_thread(session, msg, e)
|
99
|
+
legacy_thread = await self._xmpp_to_legacy_thread(session, msg, e)
|
100
100
|
return session, e, legacy_thread
|
101
101
|
|
102
|
+
async def _xmpp_to_legacy_thread(
|
103
|
+
self, session: "BaseSession", msg: Message, recipient: RecipientType
|
104
|
+
):
|
105
|
+
xmpp_thread = msg["thread"]
|
106
|
+
if not xmpp_thread:
|
107
|
+
return None
|
108
|
+
|
109
|
+
if session.MESSAGE_IDS_ARE_THREAD_IDS:
|
110
|
+
return self._xmpp_msg_id_to_legacy(session, xmpp_thread)
|
111
|
+
|
112
|
+
legacy_thread_str = session.xmpp.store.sent.get_legacy_thread(
|
113
|
+
session.user_pk, xmpp_thread
|
114
|
+
)
|
115
|
+
if legacy_thread_str is not None:
|
116
|
+
return session.xmpp.LEGACY_MSG_ID_TYPE(legacy_thread_str)
|
117
|
+
async with session.thread_creation_lock:
|
118
|
+
legacy_thread = await recipient.create_thread(xmpp_thread)
|
119
|
+
session.xmpp.store.sent.set_thread(
|
120
|
+
session.user_pk, str(legacy_thread), xmpp_thread
|
121
|
+
)
|
122
|
+
return legacy_thread
|
123
|
+
|
102
124
|
|
103
125
|
def _ignore(session: "BaseSession", msg: Message):
|
104
126
|
i = msg.get_id()
|
@@ -111,28 +133,6 @@ def _ignore(session: "BaseSession", msg: Message):
|
|
111
133
|
return True
|
112
134
|
|
113
135
|
|
114
|
-
async def _xmpp_to_legacy_thread(
|
115
|
-
session: "BaseSession", msg: Message, recipient: RecipientType
|
116
|
-
):
|
117
|
-
xmpp_thread = msg["thread"]
|
118
|
-
if not xmpp_thread:
|
119
|
-
return
|
120
|
-
|
121
|
-
if session.MESSAGE_IDS_ARE_THREAD_IDS:
|
122
|
-
return session.xmpp.store.sent.get_legacy_thread(session.user_pk, xmpp_thread)
|
123
|
-
|
124
|
-
async with session.thread_creation_lock:
|
125
|
-
legacy_thread_str = session.xmpp.store.sent.get_legacy_thread(
|
126
|
-
session.user_pk, xmpp_thread
|
127
|
-
)
|
128
|
-
if legacy_thread_str is None:
|
129
|
-
legacy_thread = str(await recipient.create_thread(xmpp_thread))
|
130
|
-
session.xmpp.store.sent.set_thread(
|
131
|
-
session.user_pk, xmpp_thread, legacy_thread
|
132
|
-
)
|
133
|
-
return session.xmpp.LEGACY_MSG_ID_TYPE(legacy_thread)
|
134
|
-
|
135
|
-
|
136
136
|
async def _get_entity(session: "BaseSession", m: Message) -> RecipientType:
|
137
137
|
session.raise_if_not_logged()
|
138
138
|
if m.get_type() == "groupchat":
|
slidge/core/mixins/attachment.py
CHANGED
@@ -33,17 +33,14 @@ from ...util.types import (
|
|
33
33
|
)
|
34
34
|
from ...util.util import fix_suffix
|
35
35
|
from .. import config
|
36
|
-
from .
|
36
|
+
from .message_text import TextMessageMixin
|
37
37
|
|
38
38
|
|
39
|
-
class AttachmentMixin(
|
39
|
+
class AttachmentMixin(TextMessageMixin):
|
40
40
|
def __init__(self, *a, **kw):
|
41
41
|
super().__init__(*a, **kw)
|
42
42
|
self.__store = self.xmpp.store.attachments
|
43
43
|
|
44
|
-
def send_text(self, *_, **k) -> Optional[Message]:
|
45
|
-
raise NotImplementedError
|
46
|
-
|
47
44
|
async def __upload(
|
48
45
|
self,
|
49
46
|
file_path: Path,
|
@@ -261,7 +258,7 @@ class AttachmentMixin(MessageMaker):
|
|
261
258
|
thumbnail["width"] = x
|
262
259
|
thumbnail["height"] = y
|
263
260
|
thumbnail["media-type"] = "image/thumbhash"
|
264
|
-
thumbnail["uri"] = "data:image/thumbhash," + urlquote(h)
|
261
|
+
thumbnail["uri"] = "data:image/thumbhash;base64," + urlquote(h)
|
265
262
|
|
266
263
|
self.__store.set_sims(uploaded_url, str(ref))
|
267
264
|
|
@@ -304,6 +301,7 @@ class AttachmentMixin(MessageMaker):
|
|
304
301
|
caption: Optional[str] = None,
|
305
302
|
carbon=False,
|
306
303
|
when: Optional[datetime] = None,
|
304
|
+
correction=False,
|
307
305
|
**kwargs,
|
308
306
|
) -> list[Message]:
|
309
307
|
msg["oob"]["url"] = uploaded_url
|
@@ -311,11 +309,19 @@ class AttachmentMixin(MessageMaker):
|
|
311
309
|
if caption:
|
312
310
|
m1 = self._send(msg, carbon=carbon, **kwargs)
|
313
311
|
m2 = self.send_text(
|
314
|
-
caption,
|
312
|
+
caption,
|
313
|
+
legacy_msg_id=legacy_msg_id,
|
314
|
+
when=when,
|
315
|
+
carbon=carbon,
|
316
|
+
correction=correction,
|
317
|
+
**kwargs,
|
315
318
|
)
|
316
319
|
return [m1, m2] if m2 else [m1]
|
317
320
|
else:
|
318
|
-
|
321
|
+
if correction:
|
322
|
+
msg["replace"]["id"] = self._replace_id(legacy_msg_id)
|
323
|
+
else:
|
324
|
+
self._set_msg_id(msg, legacy_msg_id)
|
319
325
|
return [self._send(msg, carbon=carbon, **kwargs)]
|
320
326
|
|
321
327
|
async def send_file(
|
@@ -358,6 +364,16 @@ class AttachmentMixin(MessageMaker):
|
|
358
364
|
carbon = kwargs.pop("carbon", False)
|
359
365
|
mto = kwargs.pop("mto", None)
|
360
366
|
store_multi = kwargs.pop("store_multi", True)
|
367
|
+
correction = kwargs.get("correction", False)
|
368
|
+
if correction and (original_xmpp_id := self._legacy_to_xmpp(legacy_msg_id)):
|
369
|
+
xmpp_ids = self.xmpp.store.multi.get_xmpp_ids(
|
370
|
+
self.session.user_pk, original_xmpp_id
|
371
|
+
)
|
372
|
+
|
373
|
+
for xmpp_id in xmpp_ids:
|
374
|
+
if xmpp_id == original_xmpp_id:
|
375
|
+
continue
|
376
|
+
self.retract(xmpp_id, thread)
|
361
377
|
msg = self._make_message(
|
362
378
|
when=when,
|
363
379
|
reply_to=reply_to,
|
slidge/core/mixins/lock.py
CHANGED
@@ -15,14 +15,16 @@ class NamedLockMixin:
|
|
15
15
|
locks = self.__locks
|
16
16
|
if not locks.get(id_):
|
17
17
|
locks[id_] = asyncio.Lock()
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
18
|
+
try:
|
19
|
+
async with locks[id_]:
|
20
|
+
log.trace("acquired %s", id_) # type:ignore
|
21
|
+
yield
|
22
|
+
finally:
|
23
|
+
log.trace("releasing %s", id_) # type:ignore
|
24
|
+
waiters = locks[id_]._waiters # type:ignore
|
25
|
+
if not waiters:
|
26
|
+
del locks[id_]
|
27
|
+
log.trace("erasing %s", id_) # type:ignore
|
26
28
|
|
27
29
|
def get_lock(self, id_: Hashable):
|
28
30
|
return self.__locks.get(id_)
|
slidge/core/mixins/message.py
CHANGED
@@ -1,23 +1,15 @@
|
|
1
1
|
import logging
|
2
2
|
import uuid
|
3
3
|
import warnings
|
4
|
-
from
|
5
|
-
from typing import TYPE_CHECKING, Iterable, Optional
|
4
|
+
from typing import TYPE_CHECKING, Optional
|
6
5
|
|
7
6
|
from slixmpp import Iq, Message
|
8
7
|
|
9
8
|
from ...slixfix.xep_0490.mds import PUBLISH_OPTIONS
|
10
|
-
from ...util.types import
|
11
|
-
ChatState,
|
12
|
-
LegacyMessageType,
|
13
|
-
LegacyThreadType,
|
14
|
-
LinkPreview,
|
15
|
-
Marker,
|
16
|
-
MessageReference,
|
17
|
-
ProcessingHint,
|
18
|
-
)
|
9
|
+
from ...util.types import ChatState, LegacyMessageType, Marker
|
19
10
|
from .attachment import AttachmentMixin
|
20
11
|
from .message_maker import MessageMaker
|
12
|
+
from .message_text import TextMessageMixin
|
21
13
|
|
22
14
|
if TYPE_CHECKING:
|
23
15
|
from ...group import LegacyMUC
|
@@ -158,200 +150,8 @@ class MarkerMixin(MessageMaker):
|
|
158
150
|
self.session.log.debug("Could not MDS mark", exc_info=e)
|
159
151
|
|
160
152
|
|
161
|
-
class ContentMessageMixin(AttachmentMixin):
|
162
|
-
|
163
|
-
if hints is not None:
|
164
|
-
return hints
|
165
|
-
elif self.mtype == "chat":
|
166
|
-
return {"markable", "store"}
|
167
|
-
elif self.mtype == "groupchat":
|
168
|
-
return {"markable"}
|
169
|
-
|
170
|
-
def __replace_id(self, legacy_msg_id: LegacyMessageType):
|
171
|
-
if self.mtype == "groupchat":
|
172
|
-
return self.xmpp.store.sent.get_group_xmpp_id(
|
173
|
-
self.session.user_pk, str(legacy_msg_id)
|
174
|
-
) or self._legacy_to_xmpp(legacy_msg_id)
|
175
|
-
else:
|
176
|
-
return self._legacy_to_xmpp(legacy_msg_id)
|
177
|
-
|
178
|
-
def send_text(
|
179
|
-
self,
|
180
|
-
body: str,
|
181
|
-
legacy_msg_id: Optional[LegacyMessageType] = None,
|
182
|
-
*,
|
183
|
-
when: Optional[datetime] = None,
|
184
|
-
reply_to: Optional[MessageReference] = None,
|
185
|
-
thread: Optional[LegacyThreadType] = None,
|
186
|
-
hints: Optional[Iterable[ProcessingHint]] = None,
|
187
|
-
carbon=False,
|
188
|
-
archive_only=False,
|
189
|
-
correction=False,
|
190
|
-
correction_event_id: Optional[LegacyMessageType] = None,
|
191
|
-
link_previews: Optional[list[LinkPreview]] = None,
|
192
|
-
**send_kwargs,
|
193
|
-
):
|
194
|
-
"""
|
195
|
-
Send a text message from this :term:`XMPP Entity`.
|
196
|
-
|
197
|
-
:param body: Content of the message
|
198
|
-
:param legacy_msg_id: If you want to be able to transport read markers from the gateway
|
199
|
-
user to the legacy network, specify this
|
200
|
-
:param when: when the message was sent, for a "delay" tag (:xep:`0203`)
|
201
|
-
:param reply_to: Quote another message (:xep:`0461`)
|
202
|
-
:param hints:
|
203
|
-
:param thread:
|
204
|
-
:param carbon: (only used if called on a :class:`LegacyContact`)
|
205
|
-
Set this to ``True`` if this is actually a message sent **to** the
|
206
|
-
:class:`LegacyContact` by the :term:`User`.
|
207
|
-
Use this to synchronize outgoing history for legacy official apps.
|
208
|
-
:param correction: whether this message is a correction or not
|
209
|
-
:param correction_event_id: in the case where an ID is associated with the legacy
|
210
|
-
'correction event', specify it here to use it on the XMPP side. If not specified,
|
211
|
-
a random ID will be used.
|
212
|
-
:param link_previews: A little of sender (or server, or gateway)-generated
|
213
|
-
previews of URLs linked in the body.
|
214
|
-
:param archive_only: (only in groups) Do not send this message to user,
|
215
|
-
but store it in the archive. Meant to be used during ``MUC.backfill()``
|
216
|
-
"""
|
217
|
-
if carbon and not hasattr(self, "muc"):
|
218
|
-
if not correction and self.xmpp.store.sent.was_sent_by_user(
|
219
|
-
self.session.user_pk, str(legacy_msg_id)
|
220
|
-
):
|
221
|
-
log.warning(
|
222
|
-
"Carbon message for a message an XMPP has sent? This is a bug! %s",
|
223
|
-
legacy_msg_id,
|
224
|
-
)
|
225
|
-
return
|
226
|
-
if hasattr(self, "muc") and not self.is_user: # type:ignore
|
227
|
-
log.warning(
|
228
|
-
"send_text() called with carbon=True on a participant who is not the user",
|
229
|
-
legacy_msg_id,
|
230
|
-
)
|
231
|
-
self.xmpp.store.sent.set_message(
|
232
|
-
self.session.user_pk,
|
233
|
-
str(legacy_msg_id),
|
234
|
-
self.session.legacy_to_xmpp_msg_id(legacy_msg_id),
|
235
|
-
)
|
236
|
-
hints = self.__default_hints(hints)
|
237
|
-
msg = self._make_message(
|
238
|
-
mbody=body,
|
239
|
-
legacy_msg_id=correction_event_id if correction else legacy_msg_id,
|
240
|
-
when=when,
|
241
|
-
reply_to=reply_to,
|
242
|
-
hints=hints or (),
|
243
|
-
carbon=carbon,
|
244
|
-
thread=thread,
|
245
|
-
link_previews=link_previews,
|
246
|
-
)
|
247
|
-
if correction:
|
248
|
-
msg["replace"]["id"] = self.__replace_id(legacy_msg_id)
|
249
|
-
return self._send(
|
250
|
-
msg,
|
251
|
-
archive_only=archive_only,
|
252
|
-
carbon=carbon,
|
253
|
-
legacy_msg_id=legacy_msg_id,
|
254
|
-
**send_kwargs,
|
255
|
-
)
|
256
|
-
|
257
|
-
def correct(
|
258
|
-
self,
|
259
|
-
legacy_msg_id: LegacyMessageType,
|
260
|
-
new_text: str,
|
261
|
-
*,
|
262
|
-
when: Optional[datetime] = None,
|
263
|
-
reply_to: Optional[MessageReference] = None,
|
264
|
-
thread: Optional[LegacyThreadType] = None,
|
265
|
-
hints: Optional[Iterable[ProcessingHint]] = None,
|
266
|
-
carbon=False,
|
267
|
-
archive_only=False,
|
268
|
-
correction_event_id: Optional[LegacyMessageType] = None,
|
269
|
-
link_previews: Optional[list[LinkPreview]] = None,
|
270
|
-
**send_kwargs,
|
271
|
-
):
|
272
|
-
"""
|
273
|
-
Modify a message that was previously sent by this :term:`XMPP Entity`.
|
274
|
-
|
275
|
-
Uses last message correction (:xep:`0308`)
|
276
|
-
|
277
|
-
:param new_text: New content of the message
|
278
|
-
:param legacy_msg_id: The legacy message ID of the message to correct
|
279
|
-
:param when: when the message was sent, for a "delay" tag (:xep:`0203`)
|
280
|
-
:param reply_to: Quote another message (:xep:`0461`)
|
281
|
-
:param hints:
|
282
|
-
:param thread:
|
283
|
-
:param carbon: (only in 1:1) Reflect a message sent to this ``Contact`` by the user.
|
284
|
-
Use this to synchronize outgoing history for legacy official apps.
|
285
|
-
:param archive_only: (only in groups) Do not send this message to user,
|
286
|
-
but store it in the archive. Meant to be used during ``MUC.backfill()``
|
287
|
-
:param correction_event_id: in the case where an ID is associated with the legacy
|
288
|
-
'correction event', specify it here to use it on the XMPP side. If not specified,
|
289
|
-
a random ID will be used.
|
290
|
-
:param link_previews: A little of sender (or server, or gateway)-generated
|
291
|
-
previews of URLs linked in the body.
|
292
|
-
"""
|
293
|
-
self.send_text(
|
294
|
-
new_text,
|
295
|
-
legacy_msg_id,
|
296
|
-
when=when,
|
297
|
-
reply_to=reply_to,
|
298
|
-
hints=hints,
|
299
|
-
carbon=carbon,
|
300
|
-
thread=thread,
|
301
|
-
correction=True,
|
302
|
-
archive_only=archive_only,
|
303
|
-
correction_event_id=correction_event_id,
|
304
|
-
link_previews=link_previews,
|
305
|
-
**send_kwargs,
|
306
|
-
)
|
307
|
-
|
308
|
-
def react(
|
309
|
-
self,
|
310
|
-
legacy_msg_id: LegacyMessageType,
|
311
|
-
emojis: Iterable[str] = (),
|
312
|
-
thread: Optional[LegacyThreadType] = None,
|
313
|
-
**kwargs,
|
314
|
-
):
|
315
|
-
"""
|
316
|
-
Send a reaction (:xep:`0444`) from this :term:`XMPP Entity`.
|
317
|
-
|
318
|
-
:param legacy_msg_id: The message which the reaction refers to.
|
319
|
-
:param emojis: An iterable of emojis used as reactions
|
320
|
-
:param thread:
|
321
|
-
"""
|
322
|
-
msg = self._make_message(
|
323
|
-
hints={"store"}, carbon=kwargs.get("carbon"), thread=thread
|
324
|
-
)
|
325
|
-
xmpp_id = kwargs.pop("xmpp_id", None)
|
326
|
-
if not xmpp_id:
|
327
|
-
xmpp_id = self._legacy_to_xmpp(legacy_msg_id)
|
328
|
-
self.xmpp["xep_0444"].set_reactions(msg, to_id=xmpp_id, reactions=emojis)
|
329
|
-
self._send(msg, **kwargs)
|
330
|
-
|
331
|
-
def retract(
|
332
|
-
self,
|
333
|
-
legacy_msg_id: LegacyMessageType,
|
334
|
-
thread: Optional[LegacyThreadType] = None,
|
335
|
-
**kwargs,
|
336
|
-
):
|
337
|
-
"""
|
338
|
-
Send a message retraction (:XEP:`0424`) from this :term:`XMPP Entity`.
|
339
|
-
|
340
|
-
:param legacy_msg_id: Legacy ID of the message to delete
|
341
|
-
:param thread:
|
342
|
-
"""
|
343
|
-
msg = self._make_message(
|
344
|
-
state=None,
|
345
|
-
hints={"store"},
|
346
|
-
mbody=f"/me retracted the message {legacy_msg_id}",
|
347
|
-
carbon=kwargs.get("carbon"),
|
348
|
-
thread=thread,
|
349
|
-
)
|
350
|
-
msg.enable("fallback")
|
351
|
-
# namespace version mismatch between slidge and slixmpp, update me later
|
352
|
-
msg["fallback"]["for"] = self.xmpp["xep_0424"].namespace[:-1] + "1"
|
353
|
-
msg["retract"]["id"] = msg["replace"]["id"] = self.__replace_id(legacy_msg_id)
|
354
|
-
self._send(msg, **kwargs)
|
153
|
+
class ContentMessageMixin(AttachmentMixin, TextMessageMixin):
|
154
|
+
pass
|
355
155
|
|
356
156
|
|
357
157
|
class CarbonMessageMixin(ContentMessageMixin, MarkerMixin):
|
@@ -0,0 +1,211 @@
|
|
1
|
+
import logging
|
2
|
+
from datetime import datetime
|
3
|
+
from typing import Iterable, Optional
|
4
|
+
|
5
|
+
from ...util.types import (
|
6
|
+
LegacyMessageType,
|
7
|
+
LegacyThreadType,
|
8
|
+
LinkPreview,
|
9
|
+
MessageReference,
|
10
|
+
ProcessingHint,
|
11
|
+
)
|
12
|
+
from .message_maker import MessageMaker
|
13
|
+
|
14
|
+
|
15
|
+
class TextMessageMixin(MessageMaker):
|
16
|
+
def __default_hints(self, hints: Optional[Iterable[ProcessingHint]] = None):
|
17
|
+
if hints is not None:
|
18
|
+
return hints
|
19
|
+
elif self.mtype == "chat":
|
20
|
+
return {"markable", "store"}
|
21
|
+
elif self.mtype == "groupchat":
|
22
|
+
return {"markable"}
|
23
|
+
|
24
|
+
def _replace_id(self, legacy_msg_id: LegacyMessageType):
|
25
|
+
if self.mtype == "groupchat":
|
26
|
+
return self.xmpp.store.sent.get_group_xmpp_id(
|
27
|
+
self.session.user_pk, str(legacy_msg_id)
|
28
|
+
) or self._legacy_to_xmpp(legacy_msg_id)
|
29
|
+
else:
|
30
|
+
return self._legacy_to_xmpp(legacy_msg_id)
|
31
|
+
|
32
|
+
def send_text(
|
33
|
+
self,
|
34
|
+
body: str,
|
35
|
+
legacy_msg_id: Optional[LegacyMessageType] = None,
|
36
|
+
*,
|
37
|
+
when: Optional[datetime] = None,
|
38
|
+
reply_to: Optional[MessageReference] = None,
|
39
|
+
thread: Optional[LegacyThreadType] = None,
|
40
|
+
hints: Optional[Iterable[ProcessingHint]] = None,
|
41
|
+
carbon=False,
|
42
|
+
archive_only=False,
|
43
|
+
correction=False,
|
44
|
+
correction_event_id: Optional[LegacyMessageType] = None,
|
45
|
+
link_previews: Optional[list[LinkPreview]] = None,
|
46
|
+
**send_kwargs,
|
47
|
+
):
|
48
|
+
"""
|
49
|
+
Send a text message from this :term:`XMPP Entity`.
|
50
|
+
|
51
|
+
:param body: Content of the message
|
52
|
+
:param legacy_msg_id: If you want to be able to transport read markers from the gateway
|
53
|
+
user to the legacy network, specify this
|
54
|
+
:param when: when the message was sent, for a "delay" tag (:xep:`0203`)
|
55
|
+
:param reply_to: Quote another message (:xep:`0461`)
|
56
|
+
:param hints:
|
57
|
+
:param thread:
|
58
|
+
:param carbon: (only used if called on a :class:`LegacyContact`)
|
59
|
+
Set this to ``True`` if this is actually a message sent **to** the
|
60
|
+
:class:`LegacyContact` by the :term:`User`.
|
61
|
+
Use this to synchronize outgoing history for legacy official apps.
|
62
|
+
:param correction: whether this message is a correction or not
|
63
|
+
:param correction_event_id: in the case where an ID is associated with the legacy
|
64
|
+
'correction event', specify it here to use it on the XMPP side. If not specified,
|
65
|
+
a random ID will be used.
|
66
|
+
:param link_previews: A little of sender (or server, or gateway)-generated
|
67
|
+
previews of URLs linked in the body.
|
68
|
+
:param archive_only: (only in groups) Do not send this message to user,
|
69
|
+
but store it in the archive. Meant to be used during ``MUC.backfill()``
|
70
|
+
"""
|
71
|
+
if carbon and not hasattr(self, "muc"):
|
72
|
+
if not correction and self.xmpp.store.sent.was_sent_by_user(
|
73
|
+
self.session.user_pk, str(legacy_msg_id)
|
74
|
+
):
|
75
|
+
log.warning(
|
76
|
+
"Carbon message for a message an XMPP has sent? This is a bug! %s",
|
77
|
+
legacy_msg_id,
|
78
|
+
)
|
79
|
+
return
|
80
|
+
if hasattr(self, "muc") and not self.is_user: # type:ignore
|
81
|
+
log.warning(
|
82
|
+
"send_text() called with carbon=True on a participant who is not the user",
|
83
|
+
legacy_msg_id,
|
84
|
+
)
|
85
|
+
self.xmpp.store.sent.set_message(
|
86
|
+
self.session.user_pk,
|
87
|
+
str(legacy_msg_id),
|
88
|
+
self.session.legacy_to_xmpp_msg_id(legacy_msg_id),
|
89
|
+
)
|
90
|
+
hints = self.__default_hints(hints)
|
91
|
+
msg = self._make_message(
|
92
|
+
mbody=body,
|
93
|
+
legacy_msg_id=correction_event_id if correction else legacy_msg_id,
|
94
|
+
when=when,
|
95
|
+
reply_to=reply_to,
|
96
|
+
hints=hints or (),
|
97
|
+
carbon=carbon,
|
98
|
+
thread=thread,
|
99
|
+
link_previews=link_previews,
|
100
|
+
)
|
101
|
+
if correction:
|
102
|
+
msg["replace"]["id"] = self._replace_id(legacy_msg_id)
|
103
|
+
return self._send(
|
104
|
+
msg,
|
105
|
+
archive_only=archive_only,
|
106
|
+
carbon=carbon,
|
107
|
+
legacy_msg_id=legacy_msg_id,
|
108
|
+
**send_kwargs,
|
109
|
+
)
|
110
|
+
|
111
|
+
def correct(
|
112
|
+
self,
|
113
|
+
legacy_msg_id: LegacyMessageType,
|
114
|
+
new_text: str,
|
115
|
+
*,
|
116
|
+
when: Optional[datetime] = None,
|
117
|
+
reply_to: Optional[MessageReference] = None,
|
118
|
+
thread: Optional[LegacyThreadType] = None,
|
119
|
+
hints: Optional[Iterable[ProcessingHint]] = None,
|
120
|
+
carbon=False,
|
121
|
+
archive_only=False,
|
122
|
+
correction_event_id: Optional[LegacyMessageType] = None,
|
123
|
+
link_previews: Optional[list[LinkPreview]] = None,
|
124
|
+
**send_kwargs,
|
125
|
+
):
|
126
|
+
"""
|
127
|
+
Modify a message that was previously sent by this :term:`XMPP Entity`.
|
128
|
+
|
129
|
+
Uses last message correction (:xep:`0308`)
|
130
|
+
|
131
|
+
:param new_text: New content of the message
|
132
|
+
:param legacy_msg_id: The legacy message ID of the message to correct
|
133
|
+
:param when: when the message was sent, for a "delay" tag (:xep:`0203`)
|
134
|
+
:param reply_to: Quote another message (:xep:`0461`)
|
135
|
+
:param hints:
|
136
|
+
:param thread:
|
137
|
+
:param carbon: (only in 1:1) Reflect a message sent to this ``Contact`` by the user.
|
138
|
+
Use this to synchronize outgoing history for legacy official apps.
|
139
|
+
:param archive_only: (only in groups) Do not send this message to user,
|
140
|
+
but store it in the archive. Meant to be used during ``MUC.backfill()``
|
141
|
+
:param correction_event_id: in the case where an ID is associated with the legacy
|
142
|
+
'correction event', specify it here to use it on the XMPP side. If not specified,
|
143
|
+
a random ID will be used.
|
144
|
+
:param link_previews: A little of sender (or server, or gateway)-generated
|
145
|
+
previews of URLs linked in the body.
|
146
|
+
"""
|
147
|
+
self.send_text(
|
148
|
+
new_text,
|
149
|
+
legacy_msg_id,
|
150
|
+
when=when,
|
151
|
+
reply_to=reply_to,
|
152
|
+
hints=hints,
|
153
|
+
carbon=carbon,
|
154
|
+
thread=thread,
|
155
|
+
correction=True,
|
156
|
+
archive_only=archive_only,
|
157
|
+
correction_event_id=correction_event_id,
|
158
|
+
link_previews=link_previews,
|
159
|
+
**send_kwargs,
|
160
|
+
)
|
161
|
+
|
162
|
+
def react(
|
163
|
+
self,
|
164
|
+
legacy_msg_id: LegacyMessageType,
|
165
|
+
emojis: Iterable[str] = (),
|
166
|
+
thread: Optional[LegacyThreadType] = None,
|
167
|
+
**kwargs,
|
168
|
+
):
|
169
|
+
"""
|
170
|
+
Send a reaction (:xep:`0444`) from this :term:`XMPP Entity`.
|
171
|
+
|
172
|
+
:param legacy_msg_id: The message which the reaction refers to.
|
173
|
+
:param emojis: An iterable of emojis used as reactions
|
174
|
+
:param thread:
|
175
|
+
"""
|
176
|
+
msg = self._make_message(
|
177
|
+
hints={"store"}, carbon=kwargs.get("carbon"), thread=thread
|
178
|
+
)
|
179
|
+
xmpp_id = kwargs.pop("xmpp_id", None)
|
180
|
+
if not xmpp_id:
|
181
|
+
xmpp_id = self._legacy_to_xmpp(legacy_msg_id)
|
182
|
+
self.xmpp["xep_0444"].set_reactions(msg, to_id=xmpp_id, reactions=emojis)
|
183
|
+
self._send(msg, **kwargs)
|
184
|
+
|
185
|
+
def retract(
|
186
|
+
self,
|
187
|
+
legacy_msg_id: LegacyMessageType,
|
188
|
+
thread: Optional[LegacyThreadType] = None,
|
189
|
+
**kwargs,
|
190
|
+
):
|
191
|
+
"""
|
192
|
+
Send a message retraction (:XEP:`0424`) from this :term:`XMPP Entity`.
|
193
|
+
|
194
|
+
:param legacy_msg_id: Legacy ID of the message to delete
|
195
|
+
:param thread:
|
196
|
+
"""
|
197
|
+
msg = self._make_message(
|
198
|
+
state=None,
|
199
|
+
hints={"store"},
|
200
|
+
mbody=f"/me retracted the message {legacy_msg_id}",
|
201
|
+
carbon=kwargs.get("carbon"),
|
202
|
+
thread=thread,
|
203
|
+
)
|
204
|
+
msg.enable("fallback")
|
205
|
+
# namespace version mismatch between slidge and slixmpp, update me later
|
206
|
+
msg["fallback"]["for"] = self.xmpp["xep_0424"].namespace[:-1] + "1"
|
207
|
+
msg["retract"]["id"] = msg["replace"]["id"] = self._replace_id(legacy_msg_id)
|
208
|
+
self._send(msg, **kwargs)
|
209
|
+
|
210
|
+
|
211
|
+
log = logging.getLogger(__name__)
|
@@ -0,0 +1,83 @@
|
|
1
|
+
"""Remove bogus unique constraints on room table
|
2
|
+
|
3
|
+
Revision ID: 15b0bd83407a
|
4
|
+
Revises: 45c24cc73c91
|
5
|
+
Create Date: 2024-08-28 06:57:25.022994
|
6
|
+
|
7
|
+
"""
|
8
|
+
|
9
|
+
from typing import Sequence, Union
|
10
|
+
|
11
|
+
import sqlalchemy as sa
|
12
|
+
from alembic import op
|
13
|
+
|
14
|
+
import slidge.db.meta
|
15
|
+
|
16
|
+
# revision identifiers, used by Alembic.
|
17
|
+
revision: str = "15b0bd83407a"
|
18
|
+
down_revision: Union[str, None] = "45c24cc73c91"
|
19
|
+
branch_labels: Union[str, Sequence[str], None] = None
|
20
|
+
depends_on: Union[str, Sequence[str], None] = None
|
21
|
+
|
22
|
+
meta = sa.MetaData()
|
23
|
+
room_table = sa.Table(
|
24
|
+
"room",
|
25
|
+
meta,
|
26
|
+
sa.Column("id", sa.Integer(), nullable=False),
|
27
|
+
sa.Column("user_account_id", sa.Integer(), nullable=False),
|
28
|
+
sa.Column("legacy_id", sa.String(), nullable=False),
|
29
|
+
sa.Column("jid", slidge.db.meta.JIDType(), nullable=False),
|
30
|
+
sa.Column("avatar_id", sa.Integer(), nullable=True),
|
31
|
+
sa.Column("name", sa.String(), nullable=True),
|
32
|
+
sa.Column("description", sa.String(), nullable=True),
|
33
|
+
sa.Column("subject", sa.String(), nullable=True),
|
34
|
+
sa.Column("subject_date", sa.DateTime(), nullable=True),
|
35
|
+
sa.Column("subject_setter", sa.String(), nullable=True),
|
36
|
+
sa.Column("n_participants", sa.Integer(), nullable=True),
|
37
|
+
sa.Column(
|
38
|
+
"muc_type",
|
39
|
+
sa.Enum("GROUP", "CHANNEL", "CHANNEL_NON_ANONYMOUS", name="muctype"),
|
40
|
+
nullable=True,
|
41
|
+
),
|
42
|
+
sa.Column("user_nick", sa.String(), nullable=True),
|
43
|
+
sa.Column("user_resources", sa.String(), nullable=True),
|
44
|
+
sa.Column("participants_filled", sa.Boolean(), nullable=False),
|
45
|
+
sa.Column("history_filled", sa.Boolean(), nullable=False),
|
46
|
+
sa.Column("extra_attributes", slidge.db.meta.JSONEncodedDict(), nullable=True),
|
47
|
+
sa.Column("updated", sa.Boolean(), nullable=False),
|
48
|
+
sa.Column("avatar_legacy_id", sa.String(), nullable=True),
|
49
|
+
sa.ForeignKeyConstraint(
|
50
|
+
["avatar_id"],
|
51
|
+
["avatar.id"],
|
52
|
+
),
|
53
|
+
sa.ForeignKeyConstraint(
|
54
|
+
["user_account_id"],
|
55
|
+
["user_account.id"],
|
56
|
+
),
|
57
|
+
sa.PrimaryKeyConstraint("id"),
|
58
|
+
)
|
59
|
+
|
60
|
+
|
61
|
+
def upgrade() -> None:
|
62
|
+
with op.batch_alter_table(
|
63
|
+
"room",
|
64
|
+
schema=None,
|
65
|
+
# without copy_from, the newly created table keeps the constraints
|
66
|
+
# we actually want to ditch.
|
67
|
+
copy_from=room_table,
|
68
|
+
) as batch_op:
|
69
|
+
batch_op.create_unique_constraint(
|
70
|
+
"uq_room_user_account_id_jid", ["user_account_id", "jid"]
|
71
|
+
)
|
72
|
+
batch_op.create_unique_constraint(
|
73
|
+
"uq_room_user_account_id_legacy_id", ["user_account_id", "legacy_id"]
|
74
|
+
)
|
75
|
+
|
76
|
+
|
77
|
+
def downgrade() -> None:
|
78
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
79
|
+
with op.batch_alter_table("room", schema=None) as batch_op:
|
80
|
+
batch_op.drop_constraint("uq_room_user_account_id_legacy_id", type_="unique")
|
81
|
+
batch_op.drop_constraint("uq_room_user_account_id_jid", type_="unique")
|
82
|
+
|
83
|
+
# ### end Alembic commands ###
|
@@ -4,8 +4,12 @@ Revision ID: 5bd48bfdffa2
|
|
4
4
|
Revises: b64b1a793483
|
5
5
|
Create Date: 2024-07-24 10:29:23.467851
|
6
6
|
|
7
|
+
Broken; fixed by "Remove bogus unique constraints on room table",
|
8
|
+
rev 15b0bd83407a.
|
9
|
+
|
7
10
|
"""
|
8
11
|
|
12
|
+
import logging
|
9
13
|
from typing import Sequence, Union
|
10
14
|
|
11
15
|
from alembic import op
|
@@ -26,6 +30,8 @@ def upgrade() -> None:
|
|
26
30
|
schema=None,
|
27
31
|
# without copy_from, the newly created table keeps the constraints
|
28
32
|
# we actually want to ditch.
|
33
|
+
# LATER EDIT: this actually does not work, I should have copied the
|
34
|
+
# schema in here.
|
29
35
|
copy_from=Room.__table__, # type:ignore
|
30
36
|
) as batch_op:
|
31
37
|
batch_op.create_unique_constraint(
|
@@ -35,9 +41,11 @@ def upgrade() -> None:
|
|
35
41
|
"uq_room_user_account_id_legacy_id", ["user_account_id", "legacy_id"]
|
36
42
|
)
|
37
43
|
except Exception:
|
38
|
-
#
|
44
|
+
# This only works when upgrading rev by rev because I messed up. It
|
39
45
|
# wouldn't be necessary if the constraint was named in the first place,
|
40
46
|
# cf https://alembic.sqlalchemy.org/en/latest/naming.html
|
47
|
+
# This is fixed by rev 15b0bd83407a
|
48
|
+
log.info("Skipping")
|
41
49
|
pass
|
42
50
|
|
43
51
|
|
@@ -48,3 +56,6 @@ def downgrade() -> None:
|
|
48
56
|
batch_op.drop_constraint("uq_room_user_account_id_jid", type_="unique")
|
49
57
|
|
50
58
|
# ### end Alembic commands ###
|
59
|
+
|
60
|
+
|
61
|
+
log = logging.getLogger(__name__)
|
slidge/db/models.py
CHANGED
@@ -156,7 +156,9 @@ class Contact(Base):
|
|
156
156
|
|
157
157
|
is_friend: Mapped[bool] = mapped_column(default=False)
|
158
158
|
added_to_roster: Mapped[bool] = mapped_column(default=False)
|
159
|
-
sent_order: Mapped[list["ContactSent"]] = relationship(
|
159
|
+
sent_order: Mapped[list["ContactSent"]] = relationship(
|
160
|
+
back_populates="contact", cascade="all, delete-orphan"
|
161
|
+
)
|
160
162
|
|
161
163
|
extra_attributes: Mapped[Optional[JSONSerializable]] = mapped_column(
|
162
164
|
default=None, nullable=True
|
slidge/db/store.py
CHANGED
@@ -251,15 +251,6 @@ class SentStore(EngineMixin):
|
|
251
251
|
.where(XmppToLegacyIds.type == XmppToLegacyEnum.THREAD)
|
252
252
|
).scalar()
|
253
253
|
|
254
|
-
def get_xmpp_thread(self, user_pk: int, legacy_id: str) -> Optional[str]:
|
255
|
-
with self.session() as session:
|
256
|
-
return session.execute(
|
257
|
-
select(XmppToLegacyIds.xmpp_id)
|
258
|
-
.where(XmppToLegacyIds.user_account_id == user_pk)
|
259
|
-
.where(XmppToLegacyIds.legacy_id == legacy_id)
|
260
|
-
.where(XmppToLegacyIds.type == XmppToLegacyEnum.THREAD)
|
261
|
-
).scalar()
|
262
|
-
|
263
254
|
def was_sent_by_user(self, user_pk: int, legacy_id: str) -> bool:
|
264
255
|
with self.session() as session:
|
265
256
|
return (
|
@@ -414,6 +405,16 @@ class ContactStore(UpdatedMixin):
|
|
414
405
|
|
415
406
|
def add_to_sent(self, contact_pk: int, msg_id: str) -> None:
|
416
407
|
with self.session() as session:
|
408
|
+
if (
|
409
|
+
session.query(ContactSent.id)
|
410
|
+
.where(ContactSent.contact_id == contact_pk)
|
411
|
+
.where(ContactSent.msg_id == msg_id)
|
412
|
+
.first()
|
413
|
+
) is not None:
|
414
|
+
log.warning(
|
415
|
+
"Contact %s has already sent message %s", contact_pk, msg_id
|
416
|
+
)
|
417
|
+
return
|
417
418
|
new = ContactSent(contact_id=contact_pk, msg_id=msg_id)
|
418
419
|
session.add(new)
|
419
420
|
session.commit()
|
@@ -504,6 +505,12 @@ class MAMStore(EngineMixin):
|
|
504
505
|
.where(ArchivedMessage.room_id == room_pk)
|
505
506
|
.where(ArchivedMessage.stanza_id == message.id)
|
506
507
|
).scalar()
|
508
|
+
if existing is None and legacy_msg_id is not None:
|
509
|
+
existing = session.execute(
|
510
|
+
select(ArchivedMessage)
|
511
|
+
.where(ArchivedMessage.room_id == room_pk)
|
512
|
+
.where(ArchivedMessage.legacy_id == legacy_msg_id)
|
513
|
+
).scalar()
|
507
514
|
if existing is not None:
|
508
515
|
log.debug("Updating message %s in room %s", message.id, room_pk)
|
509
516
|
existing.timestamp = message.when
|
slidge/group/bookmarks.py
CHANGED
@@ -133,6 +133,8 @@ class LegacyBookmarks(
|
|
133
133
|
try:
|
134
134
|
with muc.updating_info():
|
135
135
|
await muc.avatar_wrap_update_info()
|
136
|
+
except XMPPError:
|
137
|
+
raise
|
136
138
|
except Exception as e:
|
137
139
|
raise XMPPError("internal-server-error", str(e))
|
138
140
|
if not muc.user_nick:
|
@@ -160,6 +162,26 @@ class LegacyBookmarks(
|
|
160
162
|
" LegacyBookmarks.fill() was not overridden."
|
161
163
|
)
|
162
164
|
|
163
|
-
def remove(
|
165
|
+
async def remove(
|
166
|
+
self,
|
167
|
+
muc: LegacyMUC,
|
168
|
+
reason="You left this group from the official client.",
|
169
|
+
kick=True,
|
170
|
+
) -> None:
|
171
|
+
"""
|
172
|
+
Delete everything about a specific group.
|
173
|
+
|
174
|
+
This should be called when the user leaves the group from the official
|
175
|
+
app.
|
176
|
+
|
177
|
+
:param muc: The MUC to remove.
|
178
|
+
:param reason: Optionally, a reason why this group was removed.
|
179
|
+
:param kick: Whether the user should be kicked from this group. Set this
|
180
|
+
to False in case you do this somewhere else in your code, eg, on
|
181
|
+
receiving the confirmation that the group was deleted.
|
182
|
+
"""
|
164
183
|
assert muc.pk is not None
|
184
|
+
if kick:
|
185
|
+
user_participant = await muc.get_user_participant()
|
186
|
+
user_participant.kick(reason)
|
165
187
|
self.__store.delete(muc.pk)
|
slidge/group/participant.py
CHANGED
@@ -324,7 +324,7 @@ class LegacyParticipant(
|
|
324
324
|
) -> MessageOrPresenceTypeVar:
|
325
325
|
stanza["occupant-id"]["id"] = self.__occupant_id
|
326
326
|
self.__add_nick_element(stanza)
|
327
|
-
if isinstance(stanza, Presence):
|
327
|
+
if not self.is_user and isinstance(stanza, Presence):
|
328
328
|
if stanza["type"] == "unavailable" and not self._presence_sent:
|
329
329
|
return stanza # type:ignore
|
330
330
|
self._presence_sent = True
|
@@ -432,17 +432,17 @@ class LegacyParticipant(
|
|
432
432
|
"""
|
433
433
|
self.muc.remove_participant(self)
|
434
434
|
|
435
|
-
def kick(self):
|
435
|
+
def kick(self, reason: str | None = None):
|
436
436
|
"""
|
437
437
|
Call this when the participant is kicked from the room
|
438
438
|
"""
|
439
|
-
self.muc.remove_participant(self, kick=True)
|
439
|
+
self.muc.remove_participant(self, kick=True, reason=reason)
|
440
440
|
|
441
|
-
def ban(self):
|
441
|
+
def ban(self, reason: str | None = None):
|
442
442
|
"""
|
443
443
|
Call this when the participant is banned from the room
|
444
444
|
"""
|
445
|
-
self.muc.remove_participant(self, ban=True)
|
445
|
+
self.muc.remove_participant(self, ban=True, reason=reason)
|
446
446
|
|
447
447
|
def get_disco_info(self, jid: OptJid = None, node: Optional[str] = None):
|
448
448
|
if self.contact is not None:
|
slidge/group/room.py
CHANGED
@@ -814,13 +814,20 @@ class LegacyMUC(
|
|
814
814
|
return await self.get_user_participant(**kwargs)
|
815
815
|
return await self.get_participant_by_contact(c, **kwargs)
|
816
816
|
|
817
|
-
def remove_participant(
|
817
|
+
def remove_participant(
|
818
|
+
self,
|
819
|
+
p: "LegacyParticipantType",
|
820
|
+
kick=False,
|
821
|
+
ban=False,
|
822
|
+
reason: str | None = None,
|
823
|
+
):
|
818
824
|
"""
|
819
825
|
Call this when a participant leaves the room
|
820
826
|
|
821
827
|
:param p: The participant
|
822
828
|
:param kick: Whether the participant left because they were kicked
|
823
829
|
:param ban: Whether the participant left because they were banned
|
830
|
+
:param reason: Optionally, a reason why the participant was removed.
|
824
831
|
"""
|
825
832
|
if kick and ban:
|
826
833
|
raise TypeError("Either kick or ban")
|
@@ -834,6 +841,8 @@ class LegacyMUC(
|
|
834
841
|
presence = p._make_presence(ptype="unavailable", status_codes=codes)
|
835
842
|
p._affiliation = "outcast" if ban else "none"
|
836
843
|
p._role = "none"
|
844
|
+
if reason:
|
845
|
+
presence["muc"].set_item_attr("reason", reason)
|
837
846
|
p._send(presence)
|
838
847
|
|
839
848
|
def rename_participant(self, old_nickname: str, new_nickname: str):
|
slidge/util/test.py
CHANGED
@@ -215,13 +215,13 @@ class SlidgeTest(SlixTestPlus):
|
|
215
215
|
self.plugin, LegacyBookmarks, base_ok=True
|
216
216
|
)
|
217
217
|
|
218
|
+
# workaround for duplicate output of sql alchemy's log, cf
|
219
|
+
# https://stackoverflow.com/a/76498428/5902284
|
218
220
|
from sqlalchemy import log as sqlalchemy_log
|
219
221
|
|
220
222
|
sqlalchemy_log._add_default_handler = lambda x: None
|
221
223
|
|
222
|
-
engine = self.db_engine = create_engine(
|
223
|
-
"sqlite+pysqlite:///:memory:", echo=True
|
224
|
-
)
|
224
|
+
engine = self.db_engine = create_engine("sqlite+pysqlite:///:memory:")
|
225
225
|
Base.metadata.create_all(engine)
|
226
226
|
BaseGateway.store = SlidgeStore(engine)
|
227
227
|
BaseGateway._test_mode = True
|
@@ -293,7 +293,7 @@ class SlidgeTest(SlixTestPlus):
|
|
293
293
|
)
|
294
294
|
)
|
295
295
|
welcome = self.next_sent()
|
296
|
-
assert welcome["body"]
|
296
|
+
assert welcome["body"], welcome
|
297
297
|
stanza = self.next_sent()
|
298
298
|
assert "logging in" in stanza["status"].lower(), stanza
|
299
299
|
stanza = self.next_sent()
|
@@ -1,6 +1,6 @@
|
|
1
1
|
slidge/__init__.py,sha256=S0tUjqpZlzsr8G4Y_1Xt-KCYB07qaknTB0OwHU8k29U,1587
|
2
|
-
slidge/__main__.py,sha256=
|
3
|
-
slidge/__version__.py,sha256=
|
2
|
+
slidge/__main__.py,sha256=ydjUklOoavS4YlGfjRX_8BQN2DaSbaXPMi47RkOgcFI,37
|
3
|
+
slidge/__version__.py,sha256=9HCdKNn7jsnGlM4hiADRgDmlQEfeHOYi3XRNJ7Fe-2w,169
|
4
4
|
slidge/command/__init__.py,sha256=UYf1mjCYbZ5G7PIgaFTWSQRAzEJkQ6dTH8Fu_e_XnO0,613
|
5
5
|
slidge/command/adhoc.py,sha256=9PsTsGMPKAK_YXQpwdcH9SSDki8YQ49OZ5p65W5HA6k,9412
|
6
6
|
slidge/command/admin.py,sha256=x_kJ0TJhzf6d3OBIOXFjudZFO8bRYUG919td7OjMCug,6008
|
@@ -8,41 +8,42 @@ slidge/command/base.py,sha256=7NSzPZdBLZElrm3smzvFKgP0GUggxXdkhclxIKCjtT8,13036
|
|
8
8
|
slidge/command/categories.py,sha256=BJCfaga2qoAxnHfgHD7I_RKZuBA5nnNOukkWHJwsUFE,99
|
9
9
|
slidge/command/chat_command.py,sha256=VBs6IuDka1IyyMzz0ZyE9zMImaEzUZLcnffxq_vwb4M,10565
|
10
10
|
slidge/command/register.py,sha256=fzPcGUoJtainnDOiC13gWV-uYLuJcsmdKGJ-jXT1qIo,6697
|
11
|
-
slidge/command/user.py,sha256=
|
11
|
+
slidge/command/user.py,sha256=P4mU1wn1ywtquo0KKQsWddOhIKMV4HOueZAXOgmVvek,11700
|
12
12
|
slidge/contact/__init__.py,sha256=WMMaHk7UW7YT9EH2LtPdkU0bHQaOp4ikBhbBQskmoc8,191
|
13
13
|
slidge/contact/contact.py,sha256=kKtJ9NPLS9DPVyyahx_K-Mtp5k5UQdQJZavC1XCmWlc,23104
|
14
|
-
slidge/contact/roster.py,sha256
|
14
|
+
slidge/contact/roster.py,sha256=-Ei0f0cXX1LFpY29u4Ik68ikno3m2WRA5n5l8Nbjd_E,10267
|
15
15
|
slidge/core/__init__.py,sha256=RG7Jj5JCJERjhqJ31lOLYV-7bH_oblClQD1KF9LsTXo,68
|
16
|
-
slidge/core/config.py,sha256=
|
16
|
+
slidge/core/config.py,sha256=voRFIlVDtKTZCdjc-zbwgnngFZrGvJjJ1dxRZm0BJK0,7514
|
17
17
|
slidge/core/dispatcher/__init__.py,sha256=1EXcjXietUKlxEqdrCWCV3xZ3q_DSsjHoqWrPMbtYao,84
|
18
18
|
slidge/core/dispatcher/caps.py,sha256=vzCAXo_bhALuLEpJWtyJTzVfWx96g1AsWD8_wkoDl0Y,2028
|
19
19
|
slidge/core/dispatcher/disco.py,sha256=j56VY9NIFzwPEWFKQQZ7YIqS9GdD-ZaF_K8a2L-JvRk,2006
|
20
20
|
slidge/core/dispatcher/message/__init__.py,sha256=vpDGOc_U9XvkUU_ws9n9-5M2NPJ87XGTVpuIxM7Z99k,223
|
21
21
|
slidge/core/dispatcher/message/chat_state.py,sha256=sCdEpzbgmvBmTovNOCv9uY6v0eJZcWVvDYAGlAV3FJ4,1735
|
22
|
-
slidge/core/dispatcher/message/marker.py,sha256=
|
22
|
+
slidge/core/dispatcher/message/marker.py,sha256=f1ezaMoHupBFZY7aUMsWLAQG7G1J9b3ihxICCkpGtis,2411
|
23
23
|
slidge/core/dispatcher/message/message.py,sha256=HwauW2kGionLyDWG01OSa9a14gYzoovJuJvGbfB4nt4,15296
|
24
24
|
slidge/core/dispatcher/muc/__init__.py,sha256=V8URHLJ_y7mk-7Id6FzRuczb1Uq_Z69fhxvzHuVLH1w,269
|
25
25
|
slidge/core/dispatcher/muc/admin.py,sha256=s21V2LEqc0e_DIpipEhhQdpae762lW1lVqj4wjFhX8M,3364
|
26
26
|
slidge/core/dispatcher/muc/mam.py,sha256=1ROVP4ZPEVEH-HR5qRV4YwHz-V15uu5gyhv1ZwwKhk8,2821
|
27
|
-
slidge/core/dispatcher/muc/misc.py,sha256=
|
28
|
-
slidge/core/dispatcher/muc/owner.py,sha256=
|
27
|
+
slidge/core/dispatcher/muc/misc.py,sha256=bHBjMC-Pu3jR5hAPGMzXf-C05UbACIwg38YbJUxHIxk,4068
|
28
|
+
slidge/core/dispatcher/muc/owner.py,sha256=1a6YV7b_mmi1jC6q1ko8weeL8imQA-s-hYGPLIHd10I,3308
|
29
29
|
slidge/core/dispatcher/muc/ping.py,sha256=lb1VQPhiUPZ19KhbofRXMVCcY6wwQ2w-asnqtANaAwA,1660
|
30
30
|
slidge/core/dispatcher/presence.py,sha256=ZxAmC34yxKxbk_-h6g_S8pTssL7ovULm3q2ishpYaB4,6393
|
31
31
|
slidge/core/dispatcher/registration.py,sha256=Xmbw9NF3LUppCOa3XzreopdKDitZnwl_5HE-kds74n8,3155
|
32
32
|
slidge/core/dispatcher/search.py,sha256=9cGj0wwvyYlP_Yk440Y12sgo4Y1p-JWUDSJP5Zxch0M,3296
|
33
33
|
slidge/core/dispatcher/session_dispatcher.py,sha256=_njTftgpUKKMP-hgAo99Hu0YrIa6E9OTzSYdiMW000w,2844
|
34
|
-
slidge/core/dispatcher/util.py,sha256=
|
34
|
+
slidge/core/dispatcher/util.py,sha256=YtXyVxM3orE7aYWs-GbJumtLTI63OpaQY_t4FMTjoZo,5754
|
35
35
|
slidge/core/dispatcher/vcard.py,sha256=Rmx-wCz6Lps0mXCO48HppNQlS3GOgMuzuw9hZYBdlVU,5130
|
36
36
|
slidge/core/gateway.py,sha256=NhIgxZKPnOpwsx50OKgyZyk9nfU8ZlUSMddwIDIhFcw,36351
|
37
37
|
slidge/core/mixins/__init__.py,sha256=muReAzgvENgMvlfm0Fpe6BQFfm2EMjoDe9ZhGgo6Vig,627
|
38
|
-
slidge/core/mixins/attachment.py,sha256=
|
38
|
+
slidge/core/mixins/attachment.py,sha256=qHtv2I1buTmPO1jwRIpq2rixq5XTAljeWYj2eMWSw2k,19623
|
39
39
|
slidge/core/mixins/avatar.py,sha256=kGIIZzLSNuxF9bIvt5Bv03_uT_pU5QV1kS7cRu6-GUA,7874
|
40
40
|
slidge/core/mixins/base.py,sha256=MOd-pas38_52VawQVlxWtBtmTKC6My9G0ZaCeQxOJbs,748
|
41
41
|
slidge/core/mixins/db.py,sha256=5Qpegd7D8e5TLXLLINYcf_DuVdN-7wNmsfztUuFYPcU,442
|
42
42
|
slidge/core/mixins/disco.py,sha256=jk3Z1B6zTuisHv8VKNRJodIo0ee5btYHh2ZrlflPj_Q,3670
|
43
|
-
slidge/core/mixins/lock.py,sha256=
|
44
|
-
slidge/core/mixins/message.py,sha256=
|
43
|
+
slidge/core/mixins/lock.py,sha256=Vf1rrkbyNbSprr38WGfZiMgTB7AdbqH8ppFHY8N2yXE,975
|
44
|
+
slidge/core/mixins/message.py,sha256=FB3VoaT81xUNVnaBMSwNJoHfrVv4Iv2678yDQH-23Rw,7551
|
45
45
|
slidge/core/mixins/message_maker.py,sha256=TcCutHi0sIwL6beJNkN7XyR0aDIbA0xZyxd2Gc9ulG4,6022
|
46
|
+
slidge/core/mixins/message_text.py,sha256=pCY4tezEuwB2ZuUyUi72i4v9AJkxp_SWF1jrFsn94Ns,8096
|
46
47
|
slidge/core/mixins/presence.py,sha256=yywo6KAw8C7GaZSMrSMuioNfhW08MrnobHt8XbHd0q8,7891
|
47
48
|
slidge/core/mixins/recipient.py,sha256=U-YppozUO8pA94jmD3-qmhkykTebPNaOVWc3JDPC9w8,1302
|
48
49
|
slidge/core/pubsub.py,sha256=oTiS5KFQJAmsgkhOsvfvthT-LkuZGQSCrrUG0JskNkI,11907
|
@@ -53,12 +54,13 @@ slidge/db/alembic/env.py,sha256=hsBlRNs0zF5diSHGRSa8Fi3qRVQDA2rJdR41AEIdvxc,1642
|
|
53
54
|
slidge/db/alembic/old_user_store.py,sha256=zFOv0JEWQQK0_TMRlU4Z0G5Mc9pxvEErLyOzXmRAe5Q,5209
|
54
55
|
slidge/db/alembic/script.py.mako,sha256=MEqL-2qATlST9TAOeYgscMn1uy6HUS9NFvDgl93dMj8,635
|
55
56
|
slidge/db/alembic/versions/09f27f098baa_add_missing_attributes_in_room.py,sha256=mUL-0Io6ZPd_QbnKfwGYyjdMcM2uxQ0Wg72H23-2t_E,1033
|
57
|
+
slidge/db/alembic/versions/15b0bd83407a_remove_bogus_unique_constraints_on_room_.py,sha256=bc_H_tPCVjiiUDqWE3oQtnIvsv2tlrzt0NB2f24mbdk,2862
|
56
58
|
slidge/db/alembic/versions/2461390c0af2_store_contacts_caps_verstring_in_db.py,sha256=CLB-kOP9Rc0FJIKDLef912L5sYkjpTIPC8fhrIdrC7k,1084
|
57
59
|
slidge/db/alembic/versions/29f5280c61aa_store_subject_setter_in_room.py,sha256=f8TFS28CXjGhvIn41UYMoHYeODfqhKfo4O7gk-JwA1E,1134
|
58
60
|
slidge/db/alembic/versions/2b1f45ab7379_store_room_subject_setter_by_nickname.py,sha256=CMVP2wFz6s7t57eWdSaGtck8BXzfVPJhHE5AoWi34tI,1359
|
59
61
|
slidge/db/alembic/versions/3071e0fa69d4_add_contact_client_type.py,sha256=O5BY1vpbtuYT5j6i3EMuuJAf6loIYT1kco8c-c6TF5g,1391
|
60
62
|
slidge/db/alembic/versions/45c24cc73c91_add_bob.py,sha256=UjMySZ5LaInyPt0KbAxx0rF4GQhZh8CwBeqHtNPdG1c,1249
|
61
|
-
slidge/db/alembic/versions/5bd48bfdffa2_lift_room_legacy_id_constraint.py,sha256=
|
63
|
+
slidge/db/alembic/versions/5bd48bfdffa2_lift_room_legacy_id_constraint.py,sha256=m3USa76h0O2Xut-NePXIOZfkXl0bx0d5FyjOYpd34Jo,1977
|
62
64
|
slidge/db/alembic/versions/82a4af84b679_add_muc_history_filled.py,sha256=g37po0ydp8ZmzJrE5oFV7GscnploxjCtPDpw28SqVGk,1429
|
63
65
|
slidge/db/alembic/versions/8b993243a536_add_vcard_content_to_contact_table.py,sha256=18tG8B03Kq8Qz_-mMd28Beed6jow8XNTtrz7gT5QY3g,1210
|
64
66
|
slidge/db/alembic/versions/8d2ced764698_rely_on_db_to_store_contacts_rooms_and_.py,sha256=olXaOEEsUSasqaaKdlP1cBODsMhmV1i90qbpDM2vTm4,4696
|
@@ -70,13 +72,13 @@ slidge/db/alembic/versions/c4a8ec35a0e8_per_room_user_nick.py,sha256=jjQmlRv6nqd
|
|
70
72
|
slidge/db/alembic/versions/e91195719c2c_store_users_avatars_persistently.py,sha256=8Ga3VFgKrzMs_-B8OPtfP-0rey_MFaDg-QGtSbaft3o,640
|
71
73
|
slidge/db/avatar.py,sha256=FfRt2Vu11ZKD9F3x1_drawvUd-TDE3mp7SE3BZ9hOOg,6467
|
72
74
|
slidge/db/meta.py,sha256=v1Jf-npZ28QwdGpsLQWLBHEbEP3-jnPrygRg05tJ_Iw,1831
|
73
|
-
slidge/db/models.py,sha256=
|
74
|
-
slidge/db/store.py,sha256=
|
75
|
+
slidge/db/models.py,sha256=mazginFllRNsC2w-SW_Y9HUMtruYnzSDCGGjsJwwsp8,13782
|
76
|
+
slidge/db/store.py,sha256=7-HIml_wmgwwKe1AxI9yXbtWGz7yxH0cMc_IY4p1Wl4,46696
|
75
77
|
slidge/group/__init__.py,sha256=yFt7cHqeaKIMN6f9ZyhhspOcJJvBtLedGv-iICG7lto,258
|
76
78
|
slidge/group/archive.py,sha256=xGPkdSk8-BT6t6lNVo1FEwiFVAttoxCma8Tsyk5r8Kg,5279
|
77
|
-
slidge/group/bookmarks.py,sha256=
|
78
|
-
slidge/group/participant.py,sha256=
|
79
|
-
slidge/group/room.py,sha256=
|
79
|
+
slidge/group/bookmarks.py,sha256=AvFL34bEX6n3OP1Np309T5hrLK9GnjkjdyLJ3uiLZyc,6616
|
80
|
+
slidge/group/participant.py,sha256=Wtq03Ix55AxlK4pvYVIalLwmKklJiIAsZdeLADJNJgU,17138
|
81
|
+
slidge/group/room.py,sha256=IizSwUBoKLvcvLpDseHIW_2KAky38uWsSv-poJuCAF0,46019
|
80
82
|
slidge/main.py,sha256=8oND7xpR3eLw7b62fT61UhYlmNp_9gv3tNz2N3xR7-c,6232
|
81
83
|
slidge/migration.py,sha256=4BJmPIRB56_WIhRTqBFIIBXuvnhhBjjOMl4CE7jY6oc,1541
|
82
84
|
slidge/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -119,11 +121,11 @@ slidge/util/__init__.py,sha256=BELovoTMPcPPGz3D48esBr8A4BRRHXTvavfgnArBgEc,301
|
|
119
121
|
slidge/util/archive_msg.py,sha256=xXAR0BI5r3d6KKWjae9594izCOv6iI03z2WLuTecNw8,1724
|
120
122
|
slidge/util/conf.py,sha256=1j2OnOsCBar1tOObErhXR5RC3Vl3faliOZ1U8J3My58,6613
|
121
123
|
slidge/util/db.py,sha256=4LxZj8oBYgiSnyBUnF_ALjr0TblkfNQq_p28sCfkHMY,242
|
122
|
-
slidge/util/test.py,sha256=
|
124
|
+
slidge/util/test.py,sha256=xnGXK0wvua49ncQm4linIfH24Ux6oCkm5A71k2V80zI,14007
|
123
125
|
slidge/util/types.py,sha256=R_xfS5mRL0XUJIoDpnaAkZlTOoLPerduXBFftaVwIAI,5489
|
124
126
|
slidge/util/util.py,sha256=DyJWO2pmE-RiB9Rsy6TUTcvB-BDlmLZBW4PELx4arFQ,9156
|
125
|
-
slidge-0.2.
|
126
|
-
slidge-0.2.
|
127
|
-
slidge-0.2.
|
128
|
-
slidge-0.2.
|
129
|
-
slidge-0.2.
|
127
|
+
slidge-0.2.0b0.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
|
128
|
+
slidge-0.2.0b0.dist-info/METADATA,sha256=JrkUpw6lNEWr3ESfNKv-NMuvLPIwqz-3G2Gt7i9nkkk,5005
|
129
|
+
slidge-0.2.0b0.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
|
130
|
+
slidge-0.2.0b0.dist-info/entry_points.txt,sha256=btz6mbzx1X6fjFWAS_Bo5qNi8PtxUsDgunt-6r4JDHw,43
|
131
|
+
slidge-0.2.0b0.dist-info/RECORD,,
|
File without changes
|
File without changes
|