slidge 0.1.2__py3-none-any.whl → 0.2.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 +3 -5
- slidge/__main__.py +2 -196
- slidge/__version__.py +5 -0
- slidge/command/adhoc.py +8 -1
- slidge/command/admin.py +5 -6
- slidge/command/base.py +1 -2
- slidge/command/register.py +32 -16
- slidge/command/user.py +85 -5
- slidge/contact/contact.py +93 -31
- slidge/contact/roster.py +54 -39
- slidge/core/config.py +13 -7
- slidge/core/gateway/base.py +139 -34
- slidge/core/gateway/disco.py +2 -4
- slidge/core/gateway/mam.py +1 -4
- slidge/core/gateway/ping.py +2 -3
- slidge/core/gateway/presence.py +1 -1
- slidge/core/gateway/registration.py +32 -21
- slidge/core/gateway/search.py +3 -5
- slidge/core/gateway/session_dispatcher.py +109 -51
- slidge/core/gateway/vcard_temp.py +6 -4
- slidge/core/mixins/__init__.py +11 -1
- slidge/core/mixins/attachment.py +15 -10
- slidge/core/mixins/avatar.py +66 -18
- slidge/core/mixins/base.py +8 -2
- slidge/core/mixins/message.py +11 -7
- slidge/core/mixins/message_maker.py +17 -9
- slidge/core/mixins/presence.py +14 -4
- slidge/core/pubsub.py +54 -212
- slidge/core/session.py +65 -33
- slidge/db/__init__.py +4 -0
- slidge/db/alembic/env.py +64 -0
- slidge/db/alembic/script.py.mako +26 -0
- slidge/db/alembic/versions/09f27f098baa_add_missing_attributes_in_room.py +36 -0
- slidge/db/alembic/versions/29f5280c61aa_store_subject_setter_in_room.py +37 -0
- slidge/db/alembic/versions/8d2ced764698_rely_on_db_to_store_contacts_rooms_and_.py +133 -0
- slidge/db/alembic/versions/aa9d82a7f6ef_db_creation.py +76 -0
- slidge/db/alembic/versions/b33993e87db3_move_everything_to_persistent_db.py +214 -0
- slidge/db/alembic/versions/e91195719c2c_store_users_avatars_persistently.py +26 -0
- slidge/db/avatar.py +224 -0
- slidge/db/meta.py +65 -0
- slidge/db/models.py +365 -0
- slidge/db/store.py +976 -0
- slidge/group/archive.py +13 -14
- slidge/group/bookmarks.py +59 -56
- slidge/group/participant.py +81 -29
- slidge/group/room.py +242 -142
- slidge/main.py +201 -0
- slidge/migration.py +30 -0
- slidge/slixfix/__init__.py +35 -2
- slidge/slixfix/roster.py +11 -4
- slidge/slixfix/xep_0292/vcard4.py +1 -0
- slidge/util/db.py +1 -47
- slidge/util/test.py +21 -4
- slidge/util/types.py +24 -4
- {slidge-0.1.2.dist-info → slidge-0.2.0a0.dist-info}/METADATA +3 -1
- slidge-0.2.0a0.dist-info/RECORD +108 -0
- slidge/core/cache.py +0 -183
- slidge/util/schema.sql +0 -126
- slidge/util/sql.py +0 -508
- slidge-0.1.2.dist-info/RECORD +0 -96
- {slidge-0.1.2.dist-info → slidge-0.2.0a0.dist-info}/LICENSE +0 -0
- {slidge-0.1.2.dist-info → slidge-0.2.0a0.dist-info}/WHEEL +0 -0
- {slidge-0.1.2.dist-info → slidge-0.2.0a0.dist-info}/entry_points.txt +0 -0
slidge/main.py
ADDED
@@ -0,0 +1,201 @@
|
|
1
|
+
"""
|
2
|
+
Slidge can be configured via CLI args, environment variables and/or INI files.
|
3
|
+
|
4
|
+
To use env vars, use this convention: ``--home-dir`` becomes ``HOME_DIR``.
|
5
|
+
|
6
|
+
Everything in ``/etc/slidge/conf.d/*`` is automatically used.
|
7
|
+
To use a plugin-specific INI file, put it in another dir,
|
8
|
+
and launch slidge with ``-c /path/to/plugin-specific.conf``.
|
9
|
+
Use the long version of the CLI arg without the double dash prefix inside this
|
10
|
+
INI file, eg ``debug=true``.
|
11
|
+
|
12
|
+
An example configuration file is available at
|
13
|
+
https://git.sr.ht/~nicoco/slidge/tree/master/item/dev/confs/slidge-example.ini
|
14
|
+
"""
|
15
|
+
|
16
|
+
import asyncio
|
17
|
+
import importlib
|
18
|
+
import logging
|
19
|
+
import os
|
20
|
+
import signal
|
21
|
+
from pathlib import Path
|
22
|
+
|
23
|
+
import configargparse
|
24
|
+
|
25
|
+
from slidge import BaseGateway
|
26
|
+
from slidge.__version__ import __version__
|
27
|
+
from slidge.core import config
|
28
|
+
from slidge.core.pubsub import PepAvatar, PepNick
|
29
|
+
from slidge.db import SlidgeStore
|
30
|
+
from slidge.db.avatar import avatar_cache
|
31
|
+
from slidge.db.meta import get_engine
|
32
|
+
from slidge.migration import migrate
|
33
|
+
from slidge.util.conf import ConfigModule
|
34
|
+
from slidge.util.db import user_store
|
35
|
+
|
36
|
+
|
37
|
+
class MainConfig(ConfigModule):
|
38
|
+
def update_dynamic_defaults(self, args):
|
39
|
+
# force=True is needed in case we call a logger before this is reached,
|
40
|
+
# or basicConfig has no effect
|
41
|
+
logging.basicConfig(
|
42
|
+
level=args.loglevel,
|
43
|
+
filename=args.log_file,
|
44
|
+
force=True,
|
45
|
+
format=args.log_format,
|
46
|
+
)
|
47
|
+
|
48
|
+
if args.home_dir is None:
|
49
|
+
args.home_dir = Path("/var/lib/slidge") / str(args.jid)
|
50
|
+
|
51
|
+
if args.user_jid_validator is None:
|
52
|
+
args.user_jid_validator = ".*@" + args.server
|
53
|
+
|
54
|
+
if args.db_url is None:
|
55
|
+
args.db_url = f"sqlite:///{args.home_dir}/slidge.sqlite"
|
56
|
+
|
57
|
+
|
58
|
+
class SigTermInterrupt(Exception):
|
59
|
+
pass
|
60
|
+
|
61
|
+
|
62
|
+
def get_configurator():
|
63
|
+
p = configargparse.ArgumentParser(
|
64
|
+
default_config_files=os.getenv(
|
65
|
+
"SLIDGE_CONF_DIR", "/etc/slidge/conf.d/*.conf"
|
66
|
+
).split(":"),
|
67
|
+
description=__doc__,
|
68
|
+
)
|
69
|
+
p.add_argument(
|
70
|
+
"-c",
|
71
|
+
"--config",
|
72
|
+
help="Path to a INI config file.",
|
73
|
+
env_var="SLIDGE_CONFIG",
|
74
|
+
is_config_file=True,
|
75
|
+
)
|
76
|
+
p.add_argument(
|
77
|
+
"-q",
|
78
|
+
"--quiet",
|
79
|
+
help="loglevel=WARNING",
|
80
|
+
action="store_const",
|
81
|
+
dest="loglevel",
|
82
|
+
const=logging.WARNING,
|
83
|
+
default=logging.INFO,
|
84
|
+
env_var="SLIDGE_QUIET",
|
85
|
+
)
|
86
|
+
p.add_argument(
|
87
|
+
"-d",
|
88
|
+
"--debug",
|
89
|
+
help="loglevel=DEBUG",
|
90
|
+
action="store_const",
|
91
|
+
dest="loglevel",
|
92
|
+
const=logging.DEBUG,
|
93
|
+
env_var="SLIDGE_DEBUG",
|
94
|
+
)
|
95
|
+
p.add_argument(
|
96
|
+
"--version",
|
97
|
+
action="version",
|
98
|
+
version=f"%(prog)s {__version__}",
|
99
|
+
)
|
100
|
+
configurator = MainConfig(config, p)
|
101
|
+
return configurator
|
102
|
+
|
103
|
+
|
104
|
+
def get_parser():
|
105
|
+
return get_configurator().parser
|
106
|
+
|
107
|
+
|
108
|
+
def configure():
|
109
|
+
configurator = get_configurator()
|
110
|
+
args, unknown_argv = configurator.set_conf()
|
111
|
+
|
112
|
+
if not (h := config.HOME_DIR).exists():
|
113
|
+
logging.info("Creating directory '%s'", h)
|
114
|
+
h.mkdir()
|
115
|
+
|
116
|
+
db_file = config.HOME_DIR / "slidge.db"
|
117
|
+
user_store.set_file(db_file, args.secret_key)
|
118
|
+
|
119
|
+
avatar_cache.set_dir(h / "slidge_avatars_v3")
|
120
|
+
|
121
|
+
config.UPLOAD_REQUESTER = config.UPLOAD_REQUESTER or config.JID.bare
|
122
|
+
|
123
|
+
return unknown_argv
|
124
|
+
|
125
|
+
|
126
|
+
def handle_sigterm(_signum, _frame):
|
127
|
+
logging.info("Caught SIGTERM")
|
128
|
+
raise SigTermInterrupt
|
129
|
+
|
130
|
+
|
131
|
+
def main():
|
132
|
+
signal.signal(signal.SIGTERM, handle_sigterm)
|
133
|
+
|
134
|
+
unknown_argv = configure()
|
135
|
+
logging.info("Starting slidge version %s", __version__)
|
136
|
+
|
137
|
+
legacy_module = importlib.import_module(config.LEGACY_MODULE)
|
138
|
+
logging.debug("Legacy module: %s", dir(legacy_module))
|
139
|
+
logging.info(
|
140
|
+
"Starting legacy module: '%s' version %s",
|
141
|
+
config.LEGACY_MODULE,
|
142
|
+
getattr(legacy_module, "__version__", "No version"),
|
143
|
+
)
|
144
|
+
|
145
|
+
if plugin_config_obj := getattr(
|
146
|
+
legacy_module, "config", getattr(legacy_module, "Config", None)
|
147
|
+
):
|
148
|
+
logging.debug("Found a config object in plugin: %r", plugin_config_obj)
|
149
|
+
ConfigModule.ENV_VAR_PREFIX += (
|
150
|
+
f"_{config.LEGACY_MODULE.split('.')[-1].upper()}_"
|
151
|
+
)
|
152
|
+
logging.debug("Env var prefix: %s", ConfigModule.ENV_VAR_PREFIX)
|
153
|
+
ConfigModule(plugin_config_obj).set_conf(unknown_argv)
|
154
|
+
else:
|
155
|
+
if unknown_argv:
|
156
|
+
raise RuntimeError("Some arguments have not been recognized", unknown_argv)
|
157
|
+
|
158
|
+
migrate()
|
159
|
+
|
160
|
+
BaseGateway.store = SlidgeStore(get_engine(config.DB_URL))
|
161
|
+
gateway: BaseGateway = BaseGateway.get_unique_subclass()()
|
162
|
+
avatar_cache.http = gateway.http
|
163
|
+
avatar_cache.store = gateway.store.avatars
|
164
|
+
avatar_cache.legacy_avatar_type = gateway.AVATAR_ID_TYPE
|
165
|
+
|
166
|
+
PepAvatar.store = gateway.store
|
167
|
+
PepNick.contact_store = gateway.store.contacts
|
168
|
+
|
169
|
+
gateway.connect()
|
170
|
+
|
171
|
+
return_code = 0
|
172
|
+
try:
|
173
|
+
gateway.loop.run_forever()
|
174
|
+
except KeyboardInterrupt:
|
175
|
+
logging.debug("Received SIGINT")
|
176
|
+
except SigTermInterrupt:
|
177
|
+
logging.debug("Received SIGTERM")
|
178
|
+
except SystemExit as e:
|
179
|
+
return_code = e.code # type: ignore
|
180
|
+
logging.debug("Exit called")
|
181
|
+
except Exception as e:
|
182
|
+
return_code = 2
|
183
|
+
logging.exception("Exception in __main__")
|
184
|
+
logging.exception(e)
|
185
|
+
finally:
|
186
|
+
if gateway.has_crashed:
|
187
|
+
if return_code != 0:
|
188
|
+
logging.warning("Return code has been set twice. Please report this.")
|
189
|
+
return_code = 3
|
190
|
+
if gateway.is_connected():
|
191
|
+
logging.debug("Gateway is connected, cleaning up")
|
192
|
+
gateway.loop.run_until_complete(asyncio.gather(*gateway.shutdown()))
|
193
|
+
gateway.disconnect()
|
194
|
+
gateway.loop.run_until_complete(gateway.disconnected)
|
195
|
+
else:
|
196
|
+
logging.debug("Gateway is not connected, no need to clean up")
|
197
|
+
avatar_cache.close()
|
198
|
+
gateway.loop.run_until_complete(gateway.http.close())
|
199
|
+
logging.info("Successful clean shut down")
|
200
|
+
logging.debug("Exiting with code %s", return_code)
|
201
|
+
exit(return_code)
|
slidge/migration.py
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
import logging
|
2
2
|
import shutil
|
3
|
+
import sys
|
4
|
+
from pathlib import Path
|
5
|
+
|
6
|
+
from alembic import command
|
7
|
+
from alembic.config import Config
|
3
8
|
|
4
9
|
from .core import config
|
5
10
|
|
@@ -11,8 +16,33 @@ def remove_avatar_cache_v1():
|
|
11
16
|
shutil.rmtree(old_dir)
|
12
17
|
|
13
18
|
|
19
|
+
def get_alembic_cfg() -> Config:
|
20
|
+
alembic_cfg = Config()
|
21
|
+
alembic_cfg.set_section_option(
|
22
|
+
"alembic",
|
23
|
+
"script_location",
|
24
|
+
str(Path(__file__).parent / "db" / "alembic"),
|
25
|
+
)
|
26
|
+
return alembic_cfg
|
27
|
+
|
28
|
+
|
14
29
|
def migrate():
|
15
30
|
remove_avatar_cache_v1()
|
31
|
+
command.upgrade(get_alembic_cfg(), "head")
|
32
|
+
|
33
|
+
|
34
|
+
def main():
|
35
|
+
"""
|
36
|
+
Updates the (dev) database in ./dev/slidge.sqlite and generates a revision
|
37
|
+
|
38
|
+
Usage: python -m slidge.migration "Revision message blah blah blah"
|
39
|
+
"""
|
40
|
+
alembic_cfg = get_alembic_cfg()
|
41
|
+
command.upgrade(alembic_cfg, "head")
|
42
|
+
command.revision(alembic_cfg, sys.argv[1], autogenerate=True)
|
16
43
|
|
17
44
|
|
18
45
|
log = logging.getLogger(__name__)
|
46
|
+
|
47
|
+
if __name__ == "__main__":
|
48
|
+
main()
|
slidge/slixfix/__init__.py
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
# This module contains patches for slixmpp; some have pending requests upstream
|
2
2
|
# and should be removed on the next slixmpp release.
|
3
|
-
|
4
|
-
|
3
|
+
import logging
|
4
|
+
from collections import defaultdict
|
5
5
|
|
6
6
|
import slixmpp.plugins
|
7
7
|
from slixmpp import Message
|
8
8
|
from slixmpp.plugins.xep_0050 import XEP_0050, Command
|
9
|
+
from slixmpp.plugins.xep_0356.privilege import _VALID_ACCESSES, XEP_0356
|
9
10
|
from slixmpp.xmlstream import StanzaBase
|
10
11
|
|
11
12
|
from . import ( # xep_0356,
|
@@ -22,6 +23,37 @@ from . import ( # xep_0356,
|
|
22
23
|
xep_0490,
|
23
24
|
)
|
24
25
|
|
26
|
+
# ruff: noqa: F401
|
27
|
+
|
28
|
+
|
29
|
+
# TODO: Remove me once https://codeberg.org/poezio/slixmpp/pulls/3541 makes it
|
30
|
+
# to a slixmpp release
|
31
|
+
def _handle_privilege(self, msg: StanzaBase):
|
32
|
+
"""
|
33
|
+
Called when the XMPP server advertise the component's privileges.
|
34
|
+
|
35
|
+
Stores the privileges in this instance's granted_privileges attribute (a dict)
|
36
|
+
and raises the privileges_advertised event
|
37
|
+
"""
|
38
|
+
permissions = self.granted_privileges[msg.get_from()]
|
39
|
+
for perm in msg["privilege"]["perms"]:
|
40
|
+
access = perm["access"]
|
41
|
+
if access == "iq":
|
42
|
+
if not perm.get_plugin("namespace", check=True):
|
43
|
+
permissions.iq = defaultdict(lambda: perm["type"])
|
44
|
+
else:
|
45
|
+
for ns in perm["namespaces"]:
|
46
|
+
permissions.iq[ns["ns"]] = ns["type"]
|
47
|
+
elif access in _VALID_ACCESSES:
|
48
|
+
setattr(permissions, access, perm["type"])
|
49
|
+
else:
|
50
|
+
log.warning("Received an invalid privileged access: %s", access)
|
51
|
+
log.debug("Privileges: %s", self.granted_privileges)
|
52
|
+
self.xmpp.event("privileges_advertised")
|
53
|
+
|
54
|
+
|
55
|
+
XEP_0356._handle_privilege = _handle_privilege
|
56
|
+
|
25
57
|
|
26
58
|
def session_bind(self, jid):
|
27
59
|
self.xmpp["xep_0030"].add_feature(Command.namespace)
|
@@ -66,3 +98,4 @@ slixmpp.plugins.PLUGINS.extend(
|
|
66
98
|
|
67
99
|
|
68
100
|
Message.reply = reply # type: ignore
|
101
|
+
log = logging.getLogger(__name__)
|
slidge/slixfix/roster.py
CHANGED
@@ -1,6 +1,11 @@
|
|
1
|
+
from typing import TYPE_CHECKING
|
2
|
+
|
1
3
|
from slixmpp import JID
|
2
4
|
|
3
|
-
from ..util.db import log
|
5
|
+
from ..util.db import log
|
6
|
+
|
7
|
+
if TYPE_CHECKING:
|
8
|
+
from .. import BaseGateway
|
4
9
|
|
5
10
|
|
6
11
|
class YesSet(set):
|
@@ -23,6 +28,9 @@ class RosterBackend:
|
|
23
28
|
This is rudimentary but the only sane way I could come up with so far.
|
24
29
|
"""
|
25
30
|
|
31
|
+
def __init__(self, xmpp: "BaseGateway"):
|
32
|
+
self.xmpp = xmpp
|
33
|
+
|
26
34
|
@staticmethod
|
27
35
|
def entries(_owner_jid, _default=None):
|
28
36
|
return YesSet()
|
@@ -31,10 +39,9 @@ class RosterBackend:
|
|
31
39
|
def save(_owner_jid, _jid, _item_state, _db_state):
|
32
40
|
pass
|
33
41
|
|
34
|
-
|
35
|
-
def load(_owner_jid, jid, _db_state):
|
42
|
+
def load(self, _owner_jid, jid, _db_state):
|
36
43
|
log.debug("Load %s", jid)
|
37
|
-
user =
|
44
|
+
user = self.xmpp.store.users.get(JID(jid))
|
38
45
|
log.debug("User %s", user)
|
39
46
|
if user is None:
|
40
47
|
return {
|
slidge/util/db.py
CHANGED
@@ -15,8 +15,6 @@ from typing import Iterable, Optional, Union
|
|
15
15
|
from pickle_secure import Pickler, Unpickler
|
16
16
|
from slixmpp import JID, Iq, Message, Presence
|
17
17
|
|
18
|
-
from .sql import db
|
19
|
-
|
20
18
|
|
21
19
|
# noinspection PyUnresolvedReferences
|
22
20
|
class EncryptedShelf(shelve.DbfilenameShelf):
|
@@ -94,10 +92,6 @@ class GatewayUser:
|
|
94
92
|
# """
|
95
93
|
return self.registration_form.get(field, default)
|
96
94
|
|
97
|
-
def commit(self):
|
98
|
-
db.user_store(self)
|
99
|
-
user_store.commit(self)
|
100
|
-
|
101
95
|
|
102
96
|
class UserStore:
|
103
97
|
"""
|
@@ -126,8 +120,6 @@ class UserStore:
|
|
126
120
|
self._users = EncryptedShelf(filename, key=secret_key)
|
127
121
|
else:
|
128
122
|
self._users = shelve.open(str(filename))
|
129
|
-
for user in self._users.values():
|
130
|
-
db.user_store(user)
|
131
123
|
log.info("Registered users in the DB: %s", list(self._users.keys()))
|
132
124
|
|
133
125
|
def get_all(self) -> Iterable[GatewayUser]:
|
@@ -138,28 +130,8 @@ class UserStore:
|
|
138
130
|
"""
|
139
131
|
return self._users.values()
|
140
132
|
|
141
|
-
def add(self, jid: JID, registration_form: dict[str, Optional[str]]):
|
142
|
-
"""
|
143
|
-
Add a user to the store.
|
144
|
-
|
145
|
-
NB: there is no reason to call this manually, as this should be covered
|
146
|
-
by slixmpp XEP-0077 and XEP-0100 plugins
|
147
|
-
|
148
|
-
:param jid: JID of the gateway user
|
149
|
-
:param registration_form: Content of the registration form (:xep:`0077`)
|
150
|
-
"""
|
151
|
-
log.debug("Adding user %s", jid)
|
152
|
-
self._users[jid.bare] = user = GatewayUser(
|
153
|
-
bare_jid=jid.bare,
|
154
|
-
registration_form=registration_form,
|
155
|
-
registration_date=datetime.datetime.now(),
|
156
|
-
)
|
157
|
-
self._users.sync()
|
158
|
-
user.commit()
|
159
|
-
log.debug("Store: %s", self._users)
|
160
|
-
|
161
133
|
def commit(self, user: GatewayUser):
|
162
|
-
self._users[user.
|
134
|
+
self._users[user.jid.bare] = user
|
163
135
|
self._users.sync()
|
164
136
|
|
165
137
|
def get(self, _gateway_jid, _node, ifrom: JID, iq) -> Optional[GatewayUser]:
|
@@ -179,24 +151,6 @@ class UserStore:
|
|
179
151
|
log.debug("Getting user %s", ifrom.bare)
|
180
152
|
return self._users.get(ifrom.bare)
|
181
153
|
|
182
|
-
def remove(self, _gateway_jid, _node, ifrom: JID, _iq):
|
183
|
-
"""
|
184
|
-
Remove a user from the store
|
185
|
-
|
186
|
-
NB: there is no reason to call this, it is used by SliXMPP internal API
|
187
|
-
"""
|
188
|
-
self.remove_by_jid(ifrom)
|
189
|
-
|
190
|
-
def remove_by_jid(self, jid: JID):
|
191
|
-
"""
|
192
|
-
Remove a user from the store, by JID
|
193
|
-
"""
|
194
|
-
j = jid.bare
|
195
|
-
log.debug("Removing user %s", j)
|
196
|
-
db.user_del(self._users[j])
|
197
|
-
del self._users[j]
|
198
|
-
self._users.sync()
|
199
|
-
|
200
154
|
def get_by_jid(self, jid: JID) -> Optional[GatewayUser]:
|
201
155
|
"""
|
202
156
|
Convenience function to get a user from their JID.
|
slidge/util/test.py
CHANGED
@@ -20,6 +20,7 @@ from slixmpp.stanza.error import Error
|
|
20
20
|
from slixmpp.test import SlixTest, TestTransport
|
21
21
|
from slixmpp.xmlstream import highlight, tostring
|
22
22
|
from slixmpp.xmlstream.matcher import MatchIDSender
|
23
|
+
from sqlalchemy import create_engine
|
23
24
|
|
24
25
|
from slidge import (
|
25
26
|
BaseGateway,
|
@@ -29,12 +30,15 @@ from slidge import (
|
|
29
30
|
LegacyMUC,
|
30
31
|
LegacyParticipant,
|
31
32
|
LegacyRoster,
|
32
|
-
user_store,
|
33
33
|
)
|
34
34
|
|
35
35
|
from ..command import Command
|
36
36
|
from ..core import config
|
37
37
|
from ..core.config import _TimedeltaSeconds
|
38
|
+
from ..core.pubsub import PepAvatar, PepNick
|
39
|
+
from ..db import SlidgeStore
|
40
|
+
from ..db.avatar import avatar_cache
|
41
|
+
from ..db.meta import Base
|
38
42
|
|
39
43
|
|
40
44
|
class SlixTestPlus(SlixTest):
|
@@ -187,10 +191,10 @@ class SlidgeTest(SlixTestPlus):
|
|
187
191
|
no_roster_push = False
|
188
192
|
upload_requester = None
|
189
193
|
ignore_delay_threshold = _TimedeltaSeconds("300")
|
194
|
+
last_seen_fallback = True
|
190
195
|
|
191
196
|
@classmethod
|
192
197
|
def setUpClass(cls):
|
193
|
-
user_store.set_file(Path(tempfile.mkdtemp()) / "test.db")
|
194
198
|
for k, v in vars(cls.Config).items():
|
195
199
|
setattr(config, k.upper(), v)
|
196
200
|
|
@@ -210,7 +214,12 @@ class SlidgeTest(SlixTestPlus):
|
|
210
214
|
)
|
211
215
|
|
212
216
|
self.xmpp = BaseGateway.get_self_or_unique_subclass()()
|
213
|
-
|
217
|
+
engine = create_engine("sqlite+pysqlite:///:memory:", echo=True)
|
218
|
+
Base.metadata.create_all(engine)
|
219
|
+
self.xmpp.store = SlidgeStore(engine)
|
220
|
+
PepNick.contact_store = self.xmpp.store.contacts
|
221
|
+
PepAvatar.store = self.xmpp.store
|
222
|
+
avatar_cache.store = self.xmpp.store.avatars
|
214
223
|
self.xmpp._always_send_everything = True
|
215
224
|
|
216
225
|
self.xmpp.connection_made(TestTransport(self.xmpp))
|
@@ -242,10 +251,18 @@ class SlidgeTest(SlixTestPlus):
|
|
242
251
|
self.xmpp.use_presence_ids = False
|
243
252
|
Error.namespace = "jabber:component:accept"
|
244
253
|
|
254
|
+
def tearDown(self):
|
255
|
+
super().tearDown()
|
256
|
+
import slidge.db.store
|
257
|
+
|
258
|
+
if slidge.db.store._session is not None:
|
259
|
+
slidge.db.store._session.commit()
|
260
|
+
slidge.db.store._session = None
|
261
|
+
Base.metadata.drop_all(self.xmpp.store._engine)
|
262
|
+
|
245
263
|
@classmethod
|
246
264
|
def tearDownClass(cls):
|
247
265
|
reset_subclasses()
|
248
|
-
user_store._users = None
|
249
266
|
|
250
267
|
|
251
268
|
def format_stanza(stanza):
|
slidge/util/types.py
CHANGED
@@ -3,6 +3,7 @@ Typing stuff
|
|
3
3
|
"""
|
4
4
|
|
5
5
|
from dataclasses import dataclass
|
6
|
+
from datetime import datetime
|
6
7
|
from enum import IntEnum
|
7
8
|
from pathlib import Path
|
8
9
|
from typing import (
|
@@ -20,14 +21,13 @@ from typing import (
|
|
20
21
|
)
|
21
22
|
|
22
23
|
from slixmpp import Message, Presence
|
23
|
-
from slixmpp.types import PresenceShows
|
24
|
+
from slixmpp.types import PresenceShows, PresenceTypes
|
24
25
|
|
25
26
|
if TYPE_CHECKING:
|
26
27
|
from ..contact import LegacyContact
|
27
28
|
from ..core.pubsub import PepItem
|
28
29
|
from ..core.session import BaseSession
|
29
30
|
from ..group.participant import LegacyMUC, LegacyParticipant
|
30
|
-
from .db import GatewayUser
|
31
31
|
|
32
32
|
AnyBaseSession = BaseSession[Any, Any]
|
33
33
|
else:
|
@@ -84,13 +84,16 @@ class MessageReference(Generic[LegacyMessageType]):
|
|
84
84
|
|
85
85
|
At the very minimum, the legacy message ID attribute must be set, but to
|
86
86
|
ensure that the quote is displayed in all XMPP clients, the author must also
|
87
|
-
be set
|
87
|
+
be set (use the string "user" if the slidge user is the author of the referenced
|
88
|
+
message).
|
88
89
|
The body is used as a fallback for XMPP clients that do not support :xep:`0461`
|
89
90
|
of that failed to find the referenced message.
|
90
91
|
"""
|
91
92
|
|
92
93
|
legacy_id: LegacyMessageType
|
93
|
-
author: Optional[Union["
|
94
|
+
author: Optional[Union[Literal["user"], "LegacyParticipant", "LegacyContact"]] = (
|
95
|
+
None
|
96
|
+
)
|
94
97
|
body: Optional[str] = None
|
95
98
|
|
96
99
|
|
@@ -178,3 +181,20 @@ class Mention(NamedTuple):
|
|
178
181
|
class Hat(NamedTuple):
|
179
182
|
uri: str
|
180
183
|
title: str
|
184
|
+
|
185
|
+
|
186
|
+
class UserPreferences(TypedDict):
|
187
|
+
sync_avatar: bool
|
188
|
+
sync_presence: bool
|
189
|
+
|
190
|
+
|
191
|
+
class MamMetadata(NamedTuple):
|
192
|
+
id: str
|
193
|
+
sent_on: datetime
|
194
|
+
|
195
|
+
|
196
|
+
class CachedPresence(NamedTuple):
|
197
|
+
last_seen: Optional[datetime] = None
|
198
|
+
ptype: Optional[PresenceTypes] = None
|
199
|
+
pstatus: Optional[str] = None
|
200
|
+
pshow: Optional[PresenceShows] = None
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: slidge
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.2.0a0
|
4
4
|
Summary: XMPP bridging framework
|
5
5
|
Home-page: https://sr.ht/~nicoco/slidge/
|
6
6
|
License: AGPL-3.0-or-later
|
@@ -15,11 +15,13 @@ Classifier: Programming Language :: Python :: 3.11
|
|
15
15
|
Requires-Dist: ConfigArgParse (>=1.5.3,<2.0.0)
|
16
16
|
Requires-Dist: Pillow (>=10,<11)
|
17
17
|
Requires-Dist: aiohttp[speedups] (>=3.8.3,<4.0.0)
|
18
|
+
Requires-Dist: alembic (>=1.13.1,<2.0.0)
|
18
19
|
Requires-Dist: blurhash-python (>=1.2.1,<2.0.0)
|
19
20
|
Requires-Dist: pickle-secure (>=0.99.9,<0.100.0)
|
20
21
|
Requires-Dist: python-magic (>=0.4.27,<0.5.0)
|
21
22
|
Requires-Dist: qrcode (>=7.4.1,<8.0.0)
|
22
23
|
Requires-Dist: slixmpp (>=1.8.5,<2.0.0)
|
24
|
+
Requires-Dist: sqlalchemy (>=2.0.29,<3.0.0)
|
23
25
|
Project-URL: Documentation, https://slidge.im/
|
24
26
|
Project-URL: Repository, https://git.sr.ht/~nicoco/slidge/
|
25
27
|
Description-Content-Type: text/markdown
|