slidge 0.1.3__py3-none-any.whl → 0.2.0a0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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 +100 -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 +77 -25
- 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.3.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.3.dist-info/RECORD +0 -96
- {slidge-0.1.3.dist-info → slidge-0.2.0a0.dist-info}/LICENSE +0 -0
- {slidge-0.1.3.dist-info → slidge-0.2.0a0.dist-info}/WHEEL +0 -0
- {slidge-0.1.3.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
|