slidge 0.2.0a10__py3-none-any.whl → 0.2.0b1__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/adhoc.py +31 -15
- slidge/command/admin.py +11 -4
- slidge/command/base.py +5 -2
- slidge/command/categories.py +13 -3
- slidge/command/chat_command.py +14 -1
- slidge/command/user.py +22 -10
- slidge/contact/roster.py +2 -0
- slidge/core/config.py +6 -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 +85 -0
- slidge/db/alembic/versions/3071e0fa69d4_add_contact_client_type.py +1 -1
- slidge/db/alembic/versions/5bd48bfdffa2_lift_room_legacy_id_constraint.py +12 -1
- slidge/db/alembic/versions/8d2ced764698_rely_on_db_to_store_contacts_rooms_and_.py +6 -0
- slidge/db/alembic/versions/abba1ae0edb3_store_avatar_legacy_id_in_the_contact_.py +7 -6
- slidge/db/alembic/versions/b64b1a793483_add_source_and_legacy_id_for_archived_.py +4 -0
- slidge/db/models.py +4 -2
- slidge/db/store.py +18 -11
- 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/util/util.py +18 -0
- {slidge-0.2.0a10.dist-info → slidge-0.2.0b1.dist-info}/METADATA +1 -1
- {slidge-0.2.0a10.dist-info → slidge-0.2.0b1.dist-info}/RECORD +36 -34
- slidge-0.2.0b1.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.0b1.dist-info}/LICENSE +0 -0
- {slidge-0.2.0a10.dist-info → slidge-0.2.0b1.dist-info}/WHEEL +0 -0
slidge/__main__.py
CHANGED
slidge/__version__.py
CHANGED
slidge/command/adhoc.py
CHANGED
@@ -9,8 +9,11 @@ from slixmpp.exceptions import XMPPError
|
|
9
9
|
from slixmpp.plugins.xep_0004 import Form as SlixForm # type: ignore[attr-defined]
|
10
10
|
from slixmpp.plugins.xep_0030.stanza.items import DiscoItems
|
11
11
|
|
12
|
+
from ..core import config
|
13
|
+
from ..util.util import strip_leading_emoji
|
12
14
|
from . import Command, CommandResponseType, Confirmation, Form, TableResult
|
13
15
|
from .base import FormField
|
16
|
+
from .categories import CommandCategory
|
14
17
|
|
15
18
|
if TYPE_CHECKING:
|
16
19
|
from ..core.gateway import BaseGateway
|
@@ -46,19 +49,19 @@ class AdhocProvider:
|
|
46
49
|
return await self.__handle_result(session, result, adhoc_session)
|
47
50
|
|
48
51
|
async def __handle_category_list(
|
49
|
-
self, category:
|
52
|
+
self, category: CommandCategory, iq: Iq, adhoc_session: AdhocSessionType
|
50
53
|
) -> AdhocSessionType:
|
51
54
|
try:
|
52
55
|
session = self.xmpp.get_session_from_stanza(iq)
|
53
56
|
except XMPPError:
|
54
57
|
session = None
|
55
|
-
commands =
|
56
|
-
for command in self._categories[category]:
|
58
|
+
commands: dict[str, Command] = {}
|
59
|
+
for command in self._categories[category.node]:
|
57
60
|
try:
|
58
61
|
command.raise_if_not_authorized(iq.get_from())
|
59
62
|
except XMPPError:
|
60
63
|
continue
|
61
|
-
commands.
|
64
|
+
commands[command.NODE] = command
|
62
65
|
if len(commands) == 0:
|
63
66
|
raise XMPPError(
|
64
67
|
"not-authorized", "There is no command you can run in this category"
|
@@ -66,7 +69,7 @@ class AdhocProvider:
|
|
66
69
|
return await self.__handle_result(
|
67
70
|
session,
|
68
71
|
Form(
|
69
|
-
category,
|
72
|
+
category.name,
|
70
73
|
"",
|
71
74
|
[
|
72
75
|
FormField(
|
@@ -74,8 +77,11 @@ class AdhocProvider:
|
|
74
77
|
label="Command",
|
75
78
|
type="list-single",
|
76
79
|
options=[
|
77
|
-
{
|
78
|
-
|
80
|
+
{
|
81
|
+
"label": strip_leading_emoji_if_needed(command.NAME),
|
82
|
+
"value": command.NODE,
|
83
|
+
}
|
84
|
+
for command in commands.values()
|
79
85
|
],
|
80
86
|
)
|
81
87
|
],
|
@@ -86,12 +92,12 @@ class AdhocProvider:
|
|
86
92
|
|
87
93
|
async def __handle_category_choice(
|
88
94
|
self,
|
89
|
-
commands:
|
95
|
+
commands: dict[str, Command],
|
90
96
|
form_values: dict[str, str],
|
91
97
|
session: "BaseSession[Any, Any]",
|
92
98
|
jid: JID,
|
93
99
|
):
|
94
|
-
command = commands[
|
100
|
+
command = commands[form_values["command"]]
|
95
101
|
result = await self.__wrap_handler(command.run, session, jid)
|
96
102
|
return result
|
97
103
|
|
@@ -207,19 +213,23 @@ class AdhocProvider:
|
|
207
213
|
self.xmpp.plugin["xep_0050"].add_command( # type: ignore[no-untyped-call]
|
208
214
|
jid=jid,
|
209
215
|
node=command.NODE,
|
210
|
-
name=command.NAME,
|
216
|
+
name=strip_leading_emoji_if_needed(command.NAME),
|
211
217
|
handler=partial(self.__wrap_initial_handler, command),
|
212
218
|
)
|
213
219
|
else:
|
214
|
-
if category
|
215
|
-
|
220
|
+
if isinstance(category, str):
|
221
|
+
category = CommandCategory(category, category)
|
222
|
+
node = category.node
|
223
|
+
name = category.name
|
224
|
+
if node not in self._categories:
|
225
|
+
self._categories[node] = list[Command]()
|
216
226
|
self.xmpp.plugin["xep_0050"].add_command( # type: ignore[no-untyped-call]
|
217
227
|
jid=jid,
|
218
|
-
node=
|
219
|
-
name=
|
228
|
+
node=node,
|
229
|
+
name=strip_leading_emoji_if_needed(name),
|
220
230
|
handler=partial(self.__handle_category_list, category),
|
221
231
|
)
|
222
|
-
self._categories[
|
232
|
+
self._categories[node].append(command)
|
223
233
|
|
224
234
|
async def get_items(self, jid: JID, node: str, iq: Iq) -> DiscoItems:
|
225
235
|
"""
|
@@ -262,4 +272,10 @@ class AdhocProvider:
|
|
262
272
|
return filtered_items
|
263
273
|
|
264
274
|
|
275
|
+
def strip_leading_emoji_if_needed(text: str) -> str:
|
276
|
+
if config.STRIP_LEADING_EMOJI_ADHOC:
|
277
|
+
return strip_leading_emoji(text)
|
278
|
+
return text
|
279
|
+
|
280
|
+
|
265
281
|
log = logging.getLogger(__name__)
|
slidge/command/admin.py
CHANGED
@@ -11,6 +11,7 @@ from slixmpp.exceptions import XMPPError
|
|
11
11
|
from ..core import config
|
12
12
|
from ..util.types import AnyBaseSession
|
13
13
|
from .base import (
|
14
|
+
NODE_PREFIX,
|
14
15
|
Command,
|
15
16
|
CommandAccess,
|
16
17
|
Confirmation,
|
@@ -21,6 +22,8 @@ from .base import (
|
|
21
22
|
)
|
22
23
|
from .categories import ADMINISTRATION
|
23
24
|
|
25
|
+
NODE_PREFIX = NODE_PREFIX + "admin/"
|
26
|
+
|
24
27
|
|
25
28
|
class AdminCommand(Command):
|
26
29
|
ACCESS = CommandAccess.ADMIN_ONLY
|
@@ -30,7 +33,8 @@ class AdminCommand(Command):
|
|
30
33
|
class ListUsers(AdminCommand):
|
31
34
|
NAME = "👤 List registered users"
|
32
35
|
HELP = "List the users registered to this gateway"
|
33
|
-
|
36
|
+
CHAT_COMMAND = "list_users"
|
37
|
+
NODE = NODE_PREFIX + CHAT_COMMAND
|
34
38
|
|
35
39
|
async def run(self, _session, _ifrom, *_):
|
36
40
|
items = []
|
@@ -51,7 +55,8 @@ class ListUsers(AdminCommand):
|
|
51
55
|
class SlidgeInfo(AdminCommand):
|
52
56
|
NAME = "ℹ️ Server information"
|
53
57
|
HELP = "List the users registered to this gateway"
|
54
|
-
|
58
|
+
CHAT_COMMAND = "info"
|
59
|
+
NODE = NODE_PREFIX + CHAT_COMMAND
|
55
60
|
ACCESS = CommandAccess.ANY
|
56
61
|
|
57
62
|
async def run(self, _session, _ifrom, *_):
|
@@ -105,7 +110,8 @@ class SlidgeInfo(AdminCommand):
|
|
105
110
|
class DeleteUser(AdminCommand):
|
106
111
|
NAME = "❌ Delete a user"
|
107
112
|
HELP = "Unregister a user from the gateway"
|
108
|
-
|
113
|
+
CHAT_COMMAND = "delete_user"
|
114
|
+
NODE = NODE_PREFIX + CHAT_COMMAND
|
109
115
|
|
110
116
|
async def run(self, _session, _ifrom, *_):
|
111
117
|
return Form(
|
@@ -141,7 +147,8 @@ class DeleteUser(AdminCommand):
|
|
141
147
|
class ChangeLoglevel(AdminCommand):
|
142
148
|
NAME = "📋 Change the verbosity of the logs"
|
143
149
|
HELP = "Set the logging level"
|
144
|
-
|
150
|
+
CHAT_COMMAND = "loglevel"
|
151
|
+
NODE = NODE_PREFIX + CHAT_COMMAND
|
145
152
|
|
146
153
|
async def run(self, _session, _ifrom, *_):
|
147
154
|
return Form(
|
slidge/command/base.py
CHANGED
@@ -25,9 +25,12 @@ from slixmpp.types import JidStr
|
|
25
25
|
from ..core import config
|
26
26
|
from ..util.types import AnyBaseSession, FieldType
|
27
27
|
|
28
|
+
NODE_PREFIX = "https://slidge.im/command/core/"
|
29
|
+
|
28
30
|
if TYPE_CHECKING:
|
29
31
|
from ..core.gateway import BaseGateway
|
30
32
|
from ..core.session import BaseSession
|
33
|
+
from .categories import CommandCategory
|
31
34
|
|
32
35
|
|
33
36
|
HandlerType = Union[
|
@@ -178,8 +181,8 @@ class Form:
|
|
178
181
|
"""
|
179
182
|
form = SlixForm() # type: ignore[no-untyped-call]
|
180
183
|
form["type"] = "form"
|
181
|
-
form["instructions"] = self.instructions
|
182
184
|
form["title"] = self.title
|
185
|
+
form["instructions"] = self.instructions
|
183
186
|
for fi in self.fields:
|
184
187
|
form.append(fi.get_xml())
|
185
188
|
return form
|
@@ -347,7 +350,7 @@ class Command(ABC):
|
|
347
350
|
Who can use this command
|
348
351
|
"""
|
349
352
|
|
350
|
-
CATEGORY: Optional[str] = None
|
353
|
+
CATEGORY: Optional[Union[str, "CommandCategory"]] = None
|
351
354
|
"""
|
352
355
|
If used, the command will be under this top-level category.
|
353
356
|
Use the same string for several commands to group them.
|
slidge/command/categories.py
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
from typing import NamedTuple
|
2
|
+
|
3
|
+
from .base import NODE_PREFIX
|
4
|
+
|
5
|
+
|
6
|
+
class CommandCategory(NamedTuple):
|
7
|
+
name: str
|
8
|
+
node: str
|
9
|
+
|
10
|
+
|
11
|
+
ADMINISTRATION = CommandCategory("🛷️ Slidge administration", NODE_PREFIX + "admin")
|
12
|
+
CONTACTS = CommandCategory("👤 Contacts", NODE_PREFIX + "contacts")
|
13
|
+
GROUPS = CommandCategory("👥 Groups", NODE_PREFIX + "groups")
|
slidge/command/chat_command.py
CHANGED
@@ -13,6 +13,7 @@ from slixmpp.exceptions import XMPPError
|
|
13
13
|
from slixmpp.types import JidStr, MessageTypes
|
14
14
|
|
15
15
|
from . import Command, CommandResponseType, Confirmation, Form, TableResult
|
16
|
+
from .categories import CommandCategory
|
16
17
|
|
17
18
|
if TYPE_CHECKING:
|
18
19
|
from ..core.gateway import BaseGateway
|
@@ -280,7 +281,19 @@ class ChatCommandProvider:
|
|
280
281
|
def _help(self, mfrom: JID):
|
281
282
|
msg = "Available commands:"
|
282
283
|
for c in sorted(
|
283
|
-
self._commands.values(),
|
284
|
+
self._commands.values(),
|
285
|
+
key=lambda co: (
|
286
|
+
(
|
287
|
+
co.CATEGORY
|
288
|
+
if isinstance(co.CATEGORY, str)
|
289
|
+
else (
|
290
|
+
co.CATEGORY.name
|
291
|
+
if isinstance(co.CATEGORY, CommandCategory)
|
292
|
+
else ""
|
293
|
+
)
|
294
|
+
),
|
295
|
+
co.CHAT_COMMAND,
|
296
|
+
),
|
284
297
|
):
|
285
298
|
try:
|
286
299
|
c.raise_if_not_authorized(mfrom)
|
slidge/command/user.py
CHANGED
@@ -26,8 +26,8 @@ if TYPE_CHECKING:
|
|
26
26
|
class Search(Command):
|
27
27
|
NAME = "🔎 Search for contacts"
|
28
28
|
HELP = "Search for contacts via this gateway"
|
29
|
-
NODE = "search"
|
30
29
|
CHAT_COMMAND = "find"
|
30
|
+
NODE = CONTACTS.node + "/" + CHAT_COMMAND
|
31
31
|
ACCESS = CommandAccess.USER_LOGGED
|
32
32
|
CATEGORY = CONTACTS
|
33
33
|
|
@@ -64,7 +64,8 @@ class SyncContacts(Command):
|
|
64
64
|
"Synchronize your XMPP roster with your legacy contacts. "
|
65
65
|
"Slidge will only add/remove/modify contacts in its dedicated roster group"
|
66
66
|
)
|
67
|
-
|
67
|
+
CHAT_COMMAND = "sync-contacts"
|
68
|
+
NODE = CONTACTS.node + "/" + CHAT_COMMAND
|
68
69
|
ACCESS = CommandAccess.USER_LOGGED
|
69
70
|
CATEGORY = CONTACTS
|
70
71
|
|
@@ -123,7 +124,8 @@ class SyncContacts(Command):
|
|
123
124
|
|
124
125
|
class ListContacts(Command):
|
125
126
|
NAME = HELP = "👤 List your legacy contacts"
|
126
|
-
|
127
|
+
CHAT_COMMAND = "contacts"
|
128
|
+
NODE = CONTACTS.node + "/" + CHAT_COMMAND
|
127
129
|
ACCESS = CommandAccess.USER_LOGGED
|
128
130
|
CATEGORY = CONTACTS
|
129
131
|
|
@@ -143,7 +145,8 @@ class ListContacts(Command):
|
|
143
145
|
|
144
146
|
class ListGroups(Command):
|
145
147
|
NAME = HELP = "👥 List your legacy groups"
|
146
|
-
|
148
|
+
CHAT_COMMAND = "groups"
|
149
|
+
NODE = GROUPS.node + "/" + CHAT_COMMAND
|
147
150
|
ACCESS = CommandAccess.USER_LOGGED
|
148
151
|
CATEGORY = GROUPS
|
149
152
|
|
@@ -162,7 +165,8 @@ class ListGroups(Command):
|
|
162
165
|
class Login(Command):
|
163
166
|
NAME = "🔐 Re-login to the legacy network"
|
164
167
|
HELP = "Login to the legacy service"
|
165
|
-
|
168
|
+
CHAT_COMMAND = "re-login"
|
169
|
+
NODE = "https://slidge.im/command/core/" + CHAT_COMMAND
|
166
170
|
|
167
171
|
ACCESS = CommandAccess.USER_NON_LOGGED
|
168
172
|
|
@@ -184,7 +188,8 @@ class Login(Command):
|
|
184
188
|
class CreateGroup(Command):
|
185
189
|
NAME = "🆕 New legacy group"
|
186
190
|
HELP = "Create a group on the legacy service"
|
187
|
-
|
191
|
+
CHAT_COMMAND = "create-group"
|
192
|
+
NODE = GROUPS.node + "/" + CHAT_COMMAND
|
188
193
|
CATEGORY = GROUPS
|
189
194
|
|
190
195
|
ACCESS = CommandAccess.USER_LOGGED
|
@@ -233,7 +238,8 @@ class CreateGroup(Command):
|
|
233
238
|
class Preferences(Command):
|
234
239
|
NAME = "⚙️ Preferences"
|
235
240
|
HELP = "Customize the gateway behaviour to your liking"
|
236
|
-
|
241
|
+
CHAT_COMMAND = "preferences"
|
242
|
+
NODE = "https://slidge.im/command/core/preferences"
|
237
243
|
ACCESS = CommandAccess.USER
|
238
244
|
|
239
245
|
async def run(
|
@@ -268,7 +274,8 @@ class Preferences(Command):
|
|
268
274
|
class Unregister(Command):
|
269
275
|
NAME = "❌ Unregister from the gateway"
|
270
276
|
HELP = "Unregister from the gateway"
|
271
|
-
|
277
|
+
CHAT_COMMAND = "unregister"
|
278
|
+
NODE = "https://slidge.im/command/core/unregister"
|
272
279
|
ACCESS = CommandAccess.USER
|
273
280
|
|
274
281
|
async def run(
|
@@ -290,7 +297,8 @@ class Unregister(Command):
|
|
290
297
|
|
291
298
|
class LeaveGroup(Command):
|
292
299
|
NAME = HELP = "❌ Leave a legacy group"
|
293
|
-
|
300
|
+
CHAT_COMMAND = "leave-group"
|
301
|
+
NODE = GROUPS.node + "/" + CHAT_COMMAND
|
294
302
|
ACCESS = CommandAccess.USER_LOGGED
|
295
303
|
CATEGORY = GROUPS
|
296
304
|
|
@@ -305,7 +313,10 @@ class LeaveGroup(Command):
|
|
305
313
|
FormField(
|
306
314
|
"group",
|
307
315
|
"Group name",
|
308
|
-
|
316
|
+
type="list-single",
|
317
|
+
options=[
|
318
|
+
{"label": g.name, "value": str(i)} for i, g in enumerate(groups)
|
319
|
+
],
|
309
320
|
)
|
310
321
|
],
|
311
322
|
handler=self.confirm, # type:ignore
|
@@ -329,3 +340,4 @@ class LeaveGroup(Command):
|
|
329
340
|
@staticmethod
|
330
341
|
async def finish(session: AnyBaseSession, _ifrom, group: LegacyMUC):
|
331
342
|
await session.on_leave_group(group.legacy_id)
|
343
|
+
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"
|
@@ -217,3 +214,9 @@ DEV_MODE__DOC = (
|
|
217
214
|
"Enables an interactive python shell via chat commands, for admins."
|
218
215
|
"Not safe to use in prod, but great during dev."
|
219
216
|
)
|
217
|
+
|
218
|
+
STRIP_LEADING_EMOJI_ADHOC = False
|
219
|
+
STRIP_LEADING_EMOJI_ADHOC__DOC = (
|
220
|
+
"Strip the leading emoji in ad-hoc command names, if present, in case you "
|
221
|
+
"are a emoji-hater."
|
222
|
+
)
|
@@ -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_)
|