slidge 0.2.0a10__py3-none-any.whl → 0.2.0b0__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/__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
|