slidge 0.2.12__py3-none-any.whl → 0.3.0a0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- slidge/__init__.py +5 -2
- slidge/command/adhoc.py +9 -3
- slidge/command/admin.py +16 -12
- slidge/command/base.py +16 -12
- slidge/command/chat_command.py +25 -16
- slidge/command/user.py +7 -8
- slidge/contact/contact.py +119 -209
- slidge/contact/roster.py +106 -105
- slidge/core/config.py +2 -43
- slidge/core/dispatcher/caps.py +9 -2
- slidge/core/dispatcher/disco.py +13 -3
- slidge/core/dispatcher/message/__init__.py +1 -1
- slidge/core/dispatcher/message/chat_state.py +17 -8
- slidge/core/dispatcher/message/marker.py +7 -5
- slidge/core/dispatcher/message/message.py +117 -92
- slidge/core/dispatcher/muc/__init__.py +1 -1
- slidge/core/dispatcher/muc/admin.py +4 -4
- slidge/core/dispatcher/muc/mam.py +10 -6
- slidge/core/dispatcher/muc/misc.py +4 -2
- slidge/core/dispatcher/muc/owner.py +5 -3
- slidge/core/dispatcher/muc/ping.py +3 -1
- slidge/core/dispatcher/presence.py +21 -15
- slidge/core/dispatcher/registration.py +20 -12
- slidge/core/dispatcher/search.py +7 -3
- slidge/core/dispatcher/session_dispatcher.py +13 -5
- slidge/core/dispatcher/util.py +37 -27
- slidge/core/dispatcher/vcard.py +7 -4
- slidge/core/gateway.py +168 -84
- slidge/core/mixins/__init__.py +1 -11
- slidge/core/mixins/attachment.py +163 -148
- slidge/core/mixins/avatar.py +100 -177
- slidge/core/mixins/db.py +50 -2
- slidge/core/mixins/message.py +19 -17
- slidge/core/mixins/message_maker.py +29 -15
- slidge/core/mixins/message_text.py +38 -30
- slidge/core/mixins/presence.py +91 -35
- slidge/core/pubsub.py +42 -47
- slidge/core/session.py +88 -57
- slidge/db/alembic/versions/0337c90c0b96_unify_legacy_xmpp_id_mappings.py +183 -0
- slidge/db/alembic/versions/4dbd23a3f868_new_avatar_store.py +56 -0
- slidge/db/alembic/versions/54ce3cde350c_use_hash_for_avatar_filenames.py +50 -0
- slidge/db/alembic/versions/58b98dacf819_refactor.py +118 -0
- slidge/db/alembic/versions/75a62b74b239_ditch_hats_table.py +74 -0
- slidge/db/avatar.py +150 -119
- slidge/db/meta.py +33 -22
- slidge/db/models.py +68 -117
- slidge/db/store.py +412 -1094
- slidge/group/archive.py +61 -54
- slidge/group/bookmarks.py +74 -55
- slidge/group/participant.py +135 -142
- slidge/group/room.py +315 -312
- slidge/main.py +28 -18
- slidge/migration.py +2 -12
- slidge/slixfix/__init__.py +20 -4
- slidge/slixfix/delivery_receipt.py +6 -4
- slidge/slixfix/link_preview/link_preview.py +1 -1
- slidge/slixfix/link_preview/stanza.py +1 -1
- slidge/slixfix/roster.py +5 -7
- slidge/slixfix/xep_0077/register.py +8 -8
- slidge/slixfix/xep_0077/stanza.py +7 -7
- slidge/slixfix/xep_0100/gateway.py +12 -13
- slidge/slixfix/xep_0153/vcard_avatar.py +1 -1
- slidge/slixfix/xep_0292/vcard4.py +1 -1
- slidge/util/archive_msg.py +11 -5
- slidge/util/conf.py +23 -20
- slidge/util/jid_escaping.py +1 -1
- slidge/{core/mixins → util}/lock.py +6 -6
- slidge/util/test.py +30 -29
- slidge/util/types.py +22 -18
- slidge/util/util.py +19 -22
- {slidge-0.2.12.dist-info → slidge-0.3.0a0.dist-info}/METADATA +1 -1
- slidge-0.3.0a0.dist-info/RECORD +117 -0
- {slidge-0.2.12.dist-info → slidge-0.3.0a0.dist-info}/WHEEL +1 -1
- slidge-0.2.12.dist-info/RECORD +0 -112
- {slidge-0.2.12.dist-info → slidge-0.3.0a0.dist-info}/entry_points.txt +0 -0
- {slidge-0.2.12.dist-info → slidge-0.3.0a0.dist-info}/licenses/LICENSE +0 -0
- {slidge-0.2.12.dist-info → slidge-0.3.0a0.dist-info}/top_level.txt +0 -0
slidge/__init__.py
CHANGED
@@ -7,6 +7,7 @@ Contains importable classes for a minimal function :term:`Legacy Module`.
|
|
7
7
|
import sys
|
8
8
|
import warnings
|
9
9
|
from importlib.metadata import PackageNotFoundError, version
|
10
|
+
from typing import Any
|
10
11
|
|
11
12
|
from . import slixfix # noqa: F401
|
12
13
|
from .command import FormField, SearchResult # noqa: F401
|
@@ -31,11 +32,13 @@ def entrypoint(module_name: str) -> None:
|
|
31
32
|
main_func(module_name)
|
32
33
|
|
33
34
|
|
34
|
-
def formatwarning(
|
35
|
+
def formatwarning(
|
36
|
+
message: Any, category: Any, filename: Any, lineno: Any, line: str = ""
|
37
|
+
) -> str:
|
35
38
|
return f"{filename}:{lineno}:{category.__name__}:{message}\n"
|
36
39
|
|
37
40
|
|
38
|
-
warnings.formatwarning = formatwarning
|
41
|
+
warnings.formatwarning = formatwarning # type:ignore
|
39
42
|
|
40
43
|
try:
|
41
44
|
__version__ = version("slidge")
|
slidge/command/adhoc.py
CHANGED
@@ -4,7 +4,7 @@ import logging
|
|
4
4
|
from functools import partial
|
5
5
|
from typing import TYPE_CHECKING, Any, Callable, Optional, Union
|
6
6
|
|
7
|
-
from slixmpp import JID, Iq
|
7
|
+
from slixmpp import JID, Iq
|
8
8
|
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
|
@@ -258,6 +258,8 @@ class AdhocProvider:
|
|
258
258
|
if not all_items:
|
259
259
|
return DiscoItems()
|
260
260
|
|
261
|
+
session = self.xmpp.get_session_from_jid(ifrom)
|
262
|
+
|
261
263
|
filtered_items = DiscoItems()
|
262
264
|
filtered_items["node"] = self.xmpp.plugin["xep_0050"].stanza.Command.namespace
|
263
265
|
for item in all_items:
|
@@ -265,7 +267,9 @@ class AdhocProvider:
|
|
265
267
|
if item["node"] in self._categories:
|
266
268
|
for command in self._categories[item["node"]]:
|
267
269
|
try:
|
268
|
-
command.raise_if_not_authorized(
|
270
|
+
command.raise_if_not_authorized(
|
271
|
+
ifrom, fetch_session=False, session=session
|
272
|
+
)
|
269
273
|
except XMPPError:
|
270
274
|
authorized = False
|
271
275
|
else:
|
@@ -273,7 +277,9 @@ class AdhocProvider:
|
|
273
277
|
break
|
274
278
|
else:
|
275
279
|
try:
|
276
|
-
self._commands[item["node"]].raise_if_not_authorized(
|
280
|
+
self._commands[item["node"]].raise_if_not_authorized(
|
281
|
+
ifrom, fetch_session=False, session=session
|
282
|
+
)
|
277
283
|
except XMPPError:
|
278
284
|
authorized = False
|
279
285
|
|
slidge/command/admin.py
CHANGED
@@ -9,6 +9,7 @@ from slixmpp import JID
|
|
9
9
|
from slixmpp.exceptions import XMPPError
|
10
10
|
|
11
11
|
from ..core import config
|
12
|
+
from ..db.models import GatewayUser
|
12
13
|
from ..util.types import AnyBaseSession
|
13
14
|
from .base import (
|
14
15
|
NODE_PREFIX,
|
@@ -38,13 +39,14 @@ class ListUsers(AdminCommand):
|
|
38
39
|
|
39
40
|
async def run(self, _session, _ifrom, *_):
|
40
41
|
items = []
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
42
|
+
with self.xmpp.store.session() as orm:
|
43
|
+
for u in orm.query(GatewayUser).all():
|
44
|
+
d = u.registration_date
|
45
|
+
if d is None:
|
46
|
+
joined = ""
|
47
|
+
else:
|
48
|
+
joined = d.isoformat(timespec="seconds")
|
49
|
+
items.append({"jid": u.jid.bare, "joined": joined})
|
48
50
|
return TableResult(
|
49
51
|
description="List of registered users",
|
50
52
|
fields=[FormField("jid", type="jid-single"), FormField("joined")],
|
@@ -59,7 +61,7 @@ class SlidgeInfo(AdminCommand):
|
|
59
61
|
NODE = NODE_PREFIX + CHAT_COMMAND
|
60
62
|
ACCESS = CommandAccess.ANY
|
61
63
|
|
62
|
-
async def run(self, _session, _ifrom, *_):
|
64
|
+
async def run(self, _session, _ifrom, *_) -> str:
|
63
65
|
start = self.xmpp.datetime_started # type:ignore
|
64
66
|
uptime = datetime.now() - start
|
65
67
|
|
@@ -125,7 +127,8 @@ class DeleteUser(AdminCommand):
|
|
125
127
|
self, form_values: FormValues, _session: AnyBaseSession, _ifrom: JID
|
126
128
|
) -> Confirmation:
|
127
129
|
jid: JID = form_values.get("jid") # type:ignore
|
128
|
-
|
130
|
+
with self.xmpp.store.session() as orm:
|
131
|
+
user = orm.query(GatewayUser).one_or_none()
|
129
132
|
if user is None:
|
130
133
|
raise XMPPError("item-not-found", text=f"There is no user '{jid}'")
|
131
134
|
|
@@ -138,7 +141,8 @@ class DeleteUser(AdminCommand):
|
|
138
141
|
async def finish(
|
139
142
|
self, _session: Optional[AnyBaseSession], _ifrom: JID, jid: JID
|
140
143
|
) -> None:
|
141
|
-
|
144
|
+
with self.xmpp.store.session() as orm:
|
145
|
+
user = orm.query(GatewayUser).one_or_none()
|
142
146
|
if user is None:
|
143
147
|
raise XMPPError("bad-request", f"{jid} has no account here!")
|
144
148
|
await self.xmpp.unregister_user(user)
|
@@ -187,10 +191,10 @@ class Exec(AdminCommand):
|
|
187
191
|
|
188
192
|
context = dict[str, Any]()
|
189
193
|
|
190
|
-
def __init__(self, xmpp):
|
194
|
+
def __init__(self, xmpp) -> None:
|
191
195
|
super().__init__(xmpp)
|
192
196
|
|
193
|
-
async def run(self, session, ifrom: JID, *args):
|
197
|
+
async def run(self, session, ifrom: JID, *args) -> str:
|
194
198
|
from contextlib import redirect_stdout
|
195
199
|
from io import StringIO
|
196
200
|
|
slidge/command/base.py
CHANGED
@@ -14,15 +14,14 @@ from typing import (
|
|
14
14
|
Union,
|
15
15
|
)
|
16
16
|
|
17
|
-
from slixmpp import JID
|
17
|
+
from slixmpp import JID
|
18
18
|
from slixmpp.exceptions import XMPPError
|
19
19
|
from slixmpp.plugins.xep_0004 import Form as SlixForm # type: ignore[attr-defined]
|
20
|
-
from slixmpp.plugins.xep_0004 import
|
21
|
-
FormField as SlixFormField, # type: ignore[attr-defined]
|
22
|
-
)
|
20
|
+
from slixmpp.plugins.xep_0004 import FormField as SlixFormField
|
23
21
|
from slixmpp.types import JidStr
|
24
22
|
|
25
23
|
from ..core import config
|
24
|
+
from ..db.models import GatewayUser
|
26
25
|
from ..util.types import AnyBaseSession, FieldType
|
27
26
|
|
28
27
|
NODE_PREFIX = "https://slidge.im/command/core/"
|
@@ -364,7 +363,7 @@ class Command(ABC):
|
|
364
363
|
|
365
364
|
subclasses = list[Type["Command"]]()
|
366
365
|
|
367
|
-
def __init__(self, xmpp: "BaseGateway"):
|
366
|
+
def __init__(self, xmpp: "BaseGateway") -> None:
|
368
367
|
self.xmpp = xmpp
|
369
368
|
|
370
369
|
def __init_subclass__(cls, **kwargs: Any) -> None:
|
@@ -388,28 +387,33 @@ class Command(ABC):
|
|
388
387
|
raise XMPPError("feature-not-implemented")
|
389
388
|
|
390
389
|
def _get_session(self, jid: JID) -> Optional["BaseSession[Any, Any]"]:
|
391
|
-
|
392
|
-
if user is None:
|
393
|
-
return None
|
394
|
-
|
395
|
-
return self.xmpp.get_session_from_user(user)
|
390
|
+
return self.xmpp.get_session_from_jid(jid)
|
396
391
|
|
397
392
|
def __can_use_command(self, jid: JID):
|
398
393
|
j = jid.bare
|
399
394
|
return self.xmpp.jid_validator.match(j) or j in config.ADMINS
|
400
395
|
|
401
|
-
def raise_if_not_authorized(
|
396
|
+
def raise_if_not_authorized(
|
397
|
+
self,
|
398
|
+
jid: JID,
|
399
|
+
fetch_session: bool = True,
|
400
|
+
session: Optional["BaseSession[Any, Any]"] = None,
|
401
|
+
) -> Optional["BaseSession[Any, Any]"]:
|
402
402
|
"""
|
403
403
|
Raise an appropriate error is jid is not authorized to use the command
|
404
404
|
|
405
405
|
:param jid: jid of the entity trying to access the command
|
406
|
+
:param fetch_session:
|
407
|
+
:param session:
|
408
|
+
|
406
409
|
:return:session of JID if it exists
|
407
410
|
"""
|
408
|
-
session = self._get_session(jid)
|
409
411
|
if not self.__can_use_command(jid):
|
410
412
|
raise XMPPError(
|
411
413
|
"bad-request", "Your JID is not allowed to use this gateway."
|
412
414
|
)
|
415
|
+
if fetch_session:
|
416
|
+
session = self._get_session(jid)
|
413
417
|
|
414
418
|
if self.ACCESS == CommandAccess.ADMIN_ONLY and not is_admin(jid):
|
415
419
|
raise XMPPError("not-authorized")
|
slidge/command/chat_command.py
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
import asyncio
|
6
6
|
import functools
|
7
7
|
import logging
|
8
|
-
from typing import TYPE_CHECKING, Callable, Literal, Optional, Union, overload
|
8
|
+
from typing import TYPE_CHECKING, Any, Callable, Literal, Optional, Union, overload
|
9
9
|
from urllib.parse import quote as url_quote
|
10
10
|
|
11
11
|
from slixmpp import JID, CoroutineCallback, Message, StanzaPath
|
@@ -22,7 +22,7 @@ if TYPE_CHECKING:
|
|
22
22
|
class ChatCommandProvider:
|
23
23
|
UNKNOWN = "Wut? I don't know that command: {}"
|
24
24
|
|
25
|
-
def __init__(self, xmpp: "BaseGateway"):
|
25
|
+
def __init__(self, xmpp: "BaseGateway") -> None:
|
26
26
|
self.xmpp = xmpp
|
27
27
|
self._keywords = list[str]()
|
28
28
|
self._commands: dict[str, Command] = {}
|
@@ -49,29 +49,36 @@ class ChatCommandProvider:
|
|
49
49
|
raise RuntimeError("There is already a command triggered by '%s'", t)
|
50
50
|
self._commands[t] = command
|
51
51
|
|
52
|
+
@overload
|
53
|
+
async def input(self, jid: JidStr, text: Optional[str] = None) -> str: ...
|
54
|
+
|
52
55
|
@overload
|
53
56
|
async def input(
|
54
|
-
self, jid: JidStr, text: Optional[str], blocking: Literal[False]
|
57
|
+
self, jid: JidStr, text: Optional[str] = None, *, blocking: Literal[False] = ...
|
55
58
|
) -> asyncio.Future[str]: ...
|
56
59
|
|
57
60
|
@overload
|
58
61
|
async def input(
|
59
62
|
self,
|
60
63
|
jid: JidStr,
|
61
|
-
text:
|
62
|
-
|
63
|
-
|
64
|
+
text: str | None = None,
|
65
|
+
*,
|
66
|
+
mtype: MessageTypes = "chat",
|
67
|
+
timeout: int = 60,
|
68
|
+
blocking: Literal[True] = True,
|
69
|
+
**msg_kwargs: Any,
|
64
70
|
) -> str: ...
|
65
71
|
|
66
72
|
async def input(
|
67
73
|
self,
|
68
|
-
jid,
|
69
|
-
text=None,
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
74
|
+
jid: JidStr,
|
75
|
+
text: str | None = None,
|
76
|
+
*,
|
77
|
+
mtype: MessageTypes = "chat",
|
78
|
+
timeout: int = 60,
|
79
|
+
blocking: bool = True,
|
80
|
+
**msg_kwargs: Any,
|
81
|
+
) -> str | asyncio.Future[str]:
|
75
82
|
"""
|
76
83
|
Request arbitrary user input using a simple chat message, and await the result.
|
77
84
|
|
@@ -269,7 +276,7 @@ class ChatCommandProvider:
|
|
269
276
|
reply["body"] = f"Error: {e}"
|
270
277
|
reply.send()
|
271
278
|
|
272
|
-
def _handle_help(self, msg: Message, *rest):
|
279
|
+
def _handle_help(self, msg: Message, *rest) -> None:
|
273
280
|
if len(rest) == 0:
|
274
281
|
reply = msg.reply()
|
275
282
|
reply["body"] = self._help(msg.get_from())
|
@@ -282,6 +289,8 @@ class ChatCommandProvider:
|
|
282
289
|
self._not_found(msg, str(rest))
|
283
290
|
|
284
291
|
def _help(self, mfrom: JID):
|
292
|
+
session = self.xmpp.get_session_from_jid(mfrom)
|
293
|
+
|
285
294
|
msg = "Available commands:"
|
286
295
|
for c in sorted(
|
287
296
|
self._commands.values(),
|
@@ -299,7 +308,7 @@ class ChatCommandProvider:
|
|
299
308
|
),
|
300
309
|
):
|
301
310
|
try:
|
302
|
-
c.raise_if_not_authorized(mfrom)
|
311
|
+
c.raise_if_not_authorized(mfrom, fetch_session=False, session=session)
|
303
312
|
except XMPPError:
|
304
313
|
continue
|
305
314
|
msg += f"\n{c.CHAT_COMMAND} -- {c.NAME}"
|
@@ -311,7 +320,7 @@ class ChatCommandProvider:
|
|
311
320
|
raise XMPPError("item-not-found", e)
|
312
321
|
|
313
322
|
|
314
|
-
def percent_encode(jid: JID):
|
323
|
+
def percent_encode(jid: JID) -> str:
|
315
324
|
return f"{url_quote(jid.user)}@{jid.server}" # type:ignore
|
316
325
|
|
317
326
|
|
slidge/command/user.py
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
from copy import deepcopy
|
3
3
|
from typing import TYPE_CHECKING, Any, Optional, Union, cast
|
4
4
|
|
5
|
-
from slixmpp import JID
|
5
|
+
from slixmpp import JID
|
6
6
|
from slixmpp.exceptions import XMPPError
|
7
7
|
|
8
8
|
from ..group.room import LegacyMUC
|
@@ -254,7 +254,7 @@ class Preferences(Command):
|
|
254
254
|
assert session is not None
|
255
255
|
current = session.user.preferences
|
256
256
|
for field in fields:
|
257
|
-
field.value = current.get(field.var) # type:ignore
|
257
|
+
field.value = current.get(field.var, field.value) # type:ignore
|
258
258
|
return Form(
|
259
259
|
title="Preferences",
|
260
260
|
instructions=self.HELP,
|
@@ -268,11 +268,12 @@ class Preferences(Command):
|
|
268
268
|
assert session is not None
|
269
269
|
user = session.user
|
270
270
|
user.preferences.update(form_values) # type:ignore
|
271
|
-
self.xmpp.store.users.update(user)
|
272
271
|
if form_values["sync_avatar"]:
|
273
272
|
await self.xmpp.fetch_user_avatar(session)
|
274
273
|
else:
|
275
|
-
|
274
|
+
user.avatar_hash = None
|
275
|
+
|
276
|
+
self.xmpp.store.users.update(user)
|
276
277
|
return "Your preferences have been updated."
|
277
278
|
|
278
279
|
|
@@ -294,9 +295,7 @@ class Unregister(Command):
|
|
294
295
|
|
295
296
|
async def unregister(self, session: Optional[AnyBaseSession], _ifrom: JID) -> str:
|
296
297
|
assert session is not None
|
297
|
-
|
298
|
-
assert user is not None
|
299
|
-
await self.xmpp.unregister_user(user)
|
298
|
+
await self.xmpp.unregister_user(session.user)
|
300
299
|
return "You are not registered anymore. Bye!"
|
301
300
|
|
302
301
|
|
@@ -343,6 +342,6 @@ class LeaveGroup(Command):
|
|
343
342
|
)
|
344
343
|
|
345
344
|
@staticmethod
|
346
|
-
async def finish(session: AnyBaseSession, _ifrom, group: LegacyMUC):
|
345
|
+
async def finish(session: AnyBaseSession, _ifrom, group: LegacyMUC) -> None:
|
347
346
|
await session.on_leave_group(group.legacy_id)
|
348
347
|
await session.bookmarks.remove(group, reason="You left this group via slidge.")
|