slidge 0.2.0a10__py3-none-any.whl → 0.2.0b1__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/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_)
|