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/main.py
CHANGED
@@ -17,6 +17,7 @@ import asyncio
|
|
17
17
|
import importlib
|
18
18
|
import inspect
|
19
19
|
import logging
|
20
|
+
import logging.config
|
20
21
|
import os
|
21
22
|
import re
|
22
23
|
import signal
|
@@ -27,7 +28,6 @@ import configargparse
|
|
27
28
|
import slidge
|
28
29
|
from slidge import BaseGateway
|
29
30
|
from slidge.core import config
|
30
|
-
from slidge.core.pubsub import PepAvatar, PepNick
|
31
31
|
from slidge.db import SlidgeStore
|
32
32
|
from slidge.db.avatar import avatar_cache
|
33
33
|
from slidge.db.meta import get_engine
|
@@ -36,15 +36,18 @@ from slidge.util.conf import ConfigModule
|
|
36
36
|
|
37
37
|
|
38
38
|
class MainConfig(ConfigModule):
|
39
|
-
def update_dynamic_defaults(self, args):
|
39
|
+
def update_dynamic_defaults(self, args: configargparse.Namespace) -> None:
|
40
40
|
# force=True is needed in case we call a logger before this is reached,
|
41
41
|
# or basicConfig has no effect
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
42
|
+
if args.log_config:
|
43
|
+
logging.config.fileConfig(args.log_config)
|
44
|
+
else:
|
45
|
+
logging.basicConfig(
|
46
|
+
level=args.loglevel,
|
47
|
+
filename=args.log_file,
|
48
|
+
force=True,
|
49
|
+
format=args.log_format,
|
50
|
+
)
|
48
51
|
|
49
52
|
if args.home_dir is None:
|
50
53
|
args.home_dir = Path("/var/lib/slidge") / str(args.jid)
|
@@ -60,7 +63,7 @@ class SigTermInterrupt(Exception):
|
|
60
63
|
pass
|
61
64
|
|
62
65
|
|
63
|
-
def get_configurator(from_entrypoint: bool = False):
|
66
|
+
def get_configurator(from_entrypoint: bool = False) -> MainConfig:
|
64
67
|
p = configargparse.ArgumentParser(
|
65
68
|
default_config_files=os.getenv(
|
66
69
|
"SLIDGE_CONF_DIR", "/etc/slidge/conf.d/*.conf"
|
@@ -74,10 +77,16 @@ def get_configurator(from_entrypoint: bool = False):
|
|
74
77
|
env_var="SLIDGE_CONFIG",
|
75
78
|
is_config_file=True,
|
76
79
|
)
|
80
|
+
p.add_argument(
|
81
|
+
"--log-config",
|
82
|
+
help="Path to a INI config file to personalise logging output. Refer to "
|
83
|
+
"<https://docs.python.org/3/library/logging.config.html#configuration-file-format> "
|
84
|
+
"for details.",
|
85
|
+
)
|
77
86
|
p.add_argument(
|
78
87
|
"-q",
|
79
88
|
"--quiet",
|
80
|
-
help="loglevel=WARNING",
|
89
|
+
help="loglevel=WARNING (unused if --log-config is specified)",
|
81
90
|
action="store_const",
|
82
91
|
dest="loglevel",
|
83
92
|
const=logging.WARNING,
|
@@ -87,7 +96,7 @@ def get_configurator(from_entrypoint: bool = False):
|
|
87
96
|
p.add_argument(
|
88
97
|
"-d",
|
89
98
|
"--debug",
|
90
|
-
help="loglevel=DEBUG",
|
99
|
+
help="loglevel=DEBUG (unused if --log-config is specified)",
|
91
100
|
action="store_const",
|
92
101
|
dest="loglevel",
|
93
102
|
const=logging.DEBUG,
|
@@ -104,11 +113,11 @@ def get_configurator(from_entrypoint: bool = False):
|
|
104
113
|
return configurator
|
105
114
|
|
106
115
|
|
107
|
-
def get_parser():
|
116
|
+
def get_parser() -> configargparse.ArgumentParser:
|
108
117
|
return get_configurator().parser
|
109
118
|
|
110
119
|
|
111
|
-
def configure(from_entrypoint: bool):
|
120
|
+
def configure(from_entrypoint: bool) -> list[str]:
|
112
121
|
configurator = get_configurator(from_entrypoint)
|
113
122
|
args, unknown_argv = configurator.set_conf()
|
114
123
|
|
@@ -121,7 +130,7 @@ def configure(from_entrypoint: bool):
|
|
121
130
|
return unknown_argv
|
122
131
|
|
123
132
|
|
124
|
-
def handle_sigterm(_signum, _frame):
|
133
|
+
def handle_sigterm(_signum: int, _frame) -> None:
|
125
134
|
logging.info("Caught SIGTERM")
|
126
135
|
raise SigTermInterrupt
|
127
136
|
|
@@ -164,15 +173,16 @@ def main(module_name: str | None = None) -> None:
|
|
164
173
|
|
165
174
|
migrate()
|
166
175
|
|
167
|
-
store = SlidgeStore(
|
176
|
+
store = SlidgeStore(
|
177
|
+
get_engine(
|
178
|
+
config.DB_URL, echo=logging.getLogger().isEnabledFor(level=logging.DEBUG)
|
179
|
+
)
|
180
|
+
)
|
168
181
|
BaseGateway.store = store
|
169
182
|
gateway: BaseGateway = BaseGateway.get_unique_subclass()()
|
170
183
|
avatar_cache.store = gateway.store.avatars
|
171
184
|
avatar_cache.set_dir(config.HOME_DIR / "slidge_avatars_v3")
|
172
185
|
|
173
|
-
PepAvatar.store = gateway.store
|
174
|
-
PepNick.contact_store = gateway.store.contacts
|
175
|
-
|
176
186
|
gateway.connect()
|
177
187
|
|
178
188
|
return_code = 0
|
slidge/migration.py
CHANGED
@@ -13,7 +13,7 @@ from .db.models import GatewayUser
|
|
13
13
|
from .db.store import SlidgeStore
|
14
14
|
|
15
15
|
|
16
|
-
def remove_avatar_cache_v1():
|
16
|
+
def remove_avatar_cache_v1() -> None:
|
17
17
|
old_dir = config.HOME_DIR / "slidge_avatars"
|
18
18
|
if old_dir.exists():
|
19
19
|
log.info("Avatar cache dir v1 found, clearing it.")
|
@@ -30,22 +30,12 @@ def get_alembic_cfg() -> Config:
|
|
30
30
|
return alembic_cfg
|
31
31
|
|
32
32
|
|
33
|
-
def remove_resource_parts_from_users() -> None:
|
34
|
-
with SlidgeStore(get_engine(config.DB_URL)).session() as orm:
|
35
|
-
for user in orm.query(GatewayUser).all():
|
36
|
-
if user.jid.resource:
|
37
|
-
user.jid = JID(user.jid.bare)
|
38
|
-
orm.add(user)
|
39
|
-
orm.commit()
|
40
|
-
|
41
|
-
|
42
33
|
def migrate() -> None:
|
43
34
|
remove_avatar_cache_v1()
|
44
35
|
command.upgrade(get_alembic_cfg(), "head")
|
45
|
-
remove_resource_parts_from_users()
|
46
36
|
|
47
37
|
|
48
|
-
def main():
|
38
|
+
def main() -> None:
|
49
39
|
"""
|
50
40
|
Updates the (dev) database in ./dev/slidge.sqlite and generates a revision
|
51
41
|
|
slidge/slixfix/__init__.py
CHANGED
@@ -7,11 +7,13 @@ import uuid
|
|
7
7
|
|
8
8
|
import slixmpp.plugins
|
9
9
|
import slixmpp.stanza.roster
|
10
|
-
from slixmpp import Message
|
10
|
+
from slixmpp import Message, register_stanza_plugin
|
11
11
|
from slixmpp.exceptions import IqError
|
12
12
|
from slixmpp.plugins.xep_0050 import XEP_0050, Command
|
13
13
|
from slixmpp.plugins.xep_0356.permissions import IqPermission
|
14
14
|
from slixmpp.plugins.xep_0356.privilege import XEP_0356
|
15
|
+
from slixmpp.plugins.xep_0385.sims import XEP_0385
|
16
|
+
from slixmpp.plugins.xep_0385.sims import stanza as stanza_sims
|
15
17
|
from slixmpp.plugins.xep_0469.stanza import NS as PINNED_NS
|
16
18
|
from slixmpp.plugins.xep_0469.stanza import Pinned
|
17
19
|
from slixmpp.xmlstream import StanzaBase
|
@@ -25,7 +27,21 @@ from . import (
|
|
25
27
|
)
|
26
28
|
|
27
29
|
|
28
|
-
def
|
30
|
+
def plugin_init(self):
|
31
|
+
register_stanza_plugin(self.xmpp["xep_0372"].stanza.Reference, stanza_sims.Sims)
|
32
|
+
register_stanza_plugin(Message, stanza_sims.Sims)
|
33
|
+
|
34
|
+
register_stanza_plugin(stanza_sims.Sims, stanza_sims.Sources)
|
35
|
+
register_stanza_plugin(stanza_sims.Sims, self.xmpp["xep_0234"].stanza.File)
|
36
|
+
register_stanza_plugin(
|
37
|
+
stanza_sims.Sources, self.xmpp["xep_0372"].stanza.Reference, iterable=True
|
38
|
+
)
|
39
|
+
|
40
|
+
|
41
|
+
XEP_0385.plugin_init = plugin_init
|
42
|
+
|
43
|
+
|
44
|
+
def set_pinned(self, val: bool) -> None:
|
29
45
|
extensions = self.parent()
|
30
46
|
if val:
|
31
47
|
extensions.enable("pinned")
|
@@ -36,7 +52,7 @@ def set_pinned(self, val: bool):
|
|
36
52
|
Pinned.set_pinned = set_pinned
|
37
53
|
|
38
54
|
|
39
|
-
def session_bind(self, jid):
|
55
|
+
def session_bind(self, jid) -> None:
|
40
56
|
self.xmpp["xep_0030"].add_feature(Command.namespace)
|
41
57
|
# awful hack to for the disco items: we need to comment this line
|
42
58
|
# related issue: https://todo.sr.ht/~nicoco/slidge/131
|
@@ -46,7 +62,7 @@ def session_bind(self, jid):
|
|
46
62
|
XEP_0050.session_bind = session_bind # type:ignore
|
47
63
|
|
48
64
|
|
49
|
-
def reply(self, body=None, clear=True):
|
65
|
+
def reply(self, body=None, clear: bool = True):
|
50
66
|
"""
|
51
67
|
Overrides slixmpp's Message.reply(), since it strips to sender's resource
|
52
68
|
for mtype=groupchat, and we do not want that, because when we raise an XMPPError,
|
@@ -15,10 +15,10 @@ if TYPE_CHECKING:
|
|
15
15
|
|
16
16
|
|
17
17
|
class DeliveryReceipt:
|
18
|
-
def __init__(self, xmpp: "BaseGateway"):
|
18
|
+
def __init__(self, xmpp: "BaseGateway") -> None:
|
19
19
|
self.xmpp = xmpp
|
20
20
|
|
21
|
-
def ack(self, msg: Message):
|
21
|
+
def ack(self, msg: Message) -> None:
|
22
22
|
"""
|
23
23
|
Send a XEP-0184 (delivery receipt) in response to a message,
|
24
24
|
if appropriate.
|
@@ -30,7 +30,9 @@ class DeliveryReceipt:
|
|
30
30
|
ack = self.make_ack(msg["id"], msg["to"], msg["from"].bare, msg["type"])
|
31
31
|
ack.send()
|
32
32
|
|
33
|
-
def make_ack(
|
33
|
+
def make_ack(
|
34
|
+
self, msg_id: str, mfrom: JID, mto: JID, mtype: MessageTypes = "chat"
|
35
|
+
) -> Message:
|
34
36
|
ack = self.xmpp.Message()
|
35
37
|
ack["type"] = mtype
|
36
38
|
ack["to"] = mto
|
@@ -38,7 +40,7 @@ class DeliveryReceipt:
|
|
38
40
|
ack["receipt"] = msg_id
|
39
41
|
return ack
|
40
42
|
|
41
|
-
def requires_receipt(self, msg: Message):
|
43
|
+
def requires_receipt(self, msg: Message) -> bool:
|
42
44
|
"""
|
43
45
|
Check if a message is eligible for a delivery receipt.
|
44
46
|
|
@@ -93,7 +93,7 @@ class SiteName(OpenGraphMixin):
|
|
93
93
|
name = plugin_attrib = "site_name"
|
94
94
|
|
95
95
|
|
96
|
-
def register_plugin():
|
96
|
+
def register_plugin() -> None:
|
97
97
|
for plugin in Title, Description, Url, Image, Type_, SiteName:
|
98
98
|
register_stanza_plugin(plugin, Title)
|
99
99
|
register_stanza_plugin(Message, LinkPreview, iterable=True)
|
slidge/slixfix/roster.py
CHANGED
@@ -12,7 +12,7 @@ class YesSet(set):
|
|
12
12
|
A pseudo-set which always test True for membership
|
13
13
|
"""
|
14
14
|
|
15
|
-
def __contains__(self, item):
|
15
|
+
def __contains__(self, item) -> bool:
|
16
16
|
log.debug("Test in")
|
17
17
|
return True
|
18
18
|
|
@@ -27,7 +27,7 @@ class RosterBackend:
|
|
27
27
|
This is rudimentary but the only sane way I could come up with so far.
|
28
28
|
"""
|
29
29
|
|
30
|
-
def __init__(self, xmpp: "BaseGateway"):
|
30
|
+
def __init__(self, xmpp: "BaseGateway") -> None:
|
31
31
|
self.xmpp = xmpp
|
32
32
|
|
33
33
|
@staticmethod
|
@@ -35,14 +35,12 @@ class RosterBackend:
|
|
35
35
|
return YesSet()
|
36
36
|
|
37
37
|
@staticmethod
|
38
|
-
def save(_owner_jid, _jid, _item_state, _db_state):
|
38
|
+
def save(_owner_jid, _jid, _item_state, _db_state) -> None:
|
39
39
|
pass
|
40
40
|
|
41
41
|
def load(self, _owner_jid, jid, _db_state):
|
42
|
-
|
43
|
-
|
44
|
-
log.debug("User %s", user)
|
45
|
-
if user is None:
|
42
|
+
session = self.xmpp.get_session_from_jid(JID(jid))
|
43
|
+
if session is None:
|
46
44
|
return {
|
47
45
|
"name": "",
|
48
46
|
"groups": [],
|
@@ -71,7 +71,7 @@ class XEP_0077(BasePlugin):
|
|
71
71
|
}
|
72
72
|
_user_store: dict[str, dict[str, str]]
|
73
73
|
|
74
|
-
def plugin_init(self):
|
74
|
+
def plugin_init(self) -> None:
|
75
75
|
register_stanza_plugin(StreamFeatures, RegisterFeature)
|
76
76
|
register_stanza_plugin(Iq, Register)
|
77
77
|
|
@@ -103,7 +103,7 @@ class XEP_0077(BasePlugin):
|
|
103
103
|
|
104
104
|
self.xmpp.add_event_handler("connected", self._force_registration)
|
105
105
|
|
106
|
-
def plugin_end(self):
|
106
|
+
def plugin_end(self) -> None:
|
107
107
|
if not self.xmpp.is_component:
|
108
108
|
self.xmpp.unregister_feature("register", self.order)
|
109
109
|
|
@@ -136,12 +136,12 @@ class XEP_0077(BasePlugin):
|
|
136
136
|
reply.set_payload(reg.xml)
|
137
137
|
return reply
|
138
138
|
|
139
|
-
def _user_validate(self, _jid, _node, ifrom, registration):
|
139
|
+
def _user_validate(self, _jid, _node, ifrom, registration) -> None:
|
140
140
|
self._user_store[ifrom.bare] = {
|
141
141
|
key: registration[key] for key in self.form_fields
|
142
142
|
}
|
143
143
|
|
144
|
-
def _user_modify(self, _jid, _node, ifrom, registration):
|
144
|
+
def _user_modify(self, _jid, _node, ifrom, registration) -> None:
|
145
145
|
self._user_store[ifrom.bare] = {
|
146
146
|
key: registration[key] for key in self.form_fields
|
147
147
|
}
|
@@ -212,11 +212,11 @@ class XEP_0077(BasePlugin):
|
|
212
212
|
else:
|
213
213
|
self.xmpp.event("user_modify", iq)
|
214
214
|
|
215
|
-
async def _send_form(self, iq):
|
215
|
+
async def _send_form(self, iq) -> None:
|
216
216
|
reply = await self.api["make_registration_form"](None, None, iq["from"], iq)
|
217
217
|
reply.send()
|
218
218
|
|
219
|
-
def _force_registration(self, _event):
|
219
|
+
def _force_registration(self, _event) -> None:
|
220
220
|
if self.force_registration:
|
221
221
|
self.xmpp.add_filter("in", self._force_stream_feature)
|
222
222
|
|
@@ -233,7 +233,7 @@ class XEP_0077(BasePlugin):
|
|
233
233
|
self.xmpp.del_filter("in", self._force_stream_feature)
|
234
234
|
return stanza_
|
235
235
|
|
236
|
-
async def _handle_register_feature(self, _features):
|
236
|
+
async def _handle_register_feature(self, _features) -> bool:
|
237
237
|
if "mechanisms" in self.xmpp.features:
|
238
238
|
# We have already logged in with an account
|
239
239
|
return False
|
@@ -276,7 +276,7 @@ class XEP_0077(BasePlugin):
|
|
276
276
|
return iq.send(timeout=timeout, callback=callback)
|
277
277
|
|
278
278
|
|
279
|
-
def _send_error(iq, code, error_type, name, text=""):
|
279
|
+
def _send_error(iq, code, error_type, name, text: str="") -> None:
|
280
280
|
# It would be nice to raise XMPPError but the iq payload
|
281
281
|
# should include the register info
|
282
282
|
reply = iq.reply()
|
@@ -57,27 +57,27 @@ class Register(ElementBase):
|
|
57
57
|
"key",
|
58
58
|
}
|
59
59
|
|
60
|
-
def get_registered(self):
|
60
|
+
def get_registered(self) -> bool:
|
61
61
|
present = self.xml.find("{%s}registered" % self.namespace)
|
62
62
|
return present is not None
|
63
63
|
|
64
|
-
def get_remove(self):
|
64
|
+
def get_remove(self) -> bool:
|
65
65
|
present = self.xml.find("{%s}remove" % self.namespace)
|
66
66
|
return present is not None
|
67
67
|
|
68
|
-
def set_registered(self, value):
|
68
|
+
def set_registered(self, value) -> None:
|
69
69
|
if value:
|
70
70
|
self.add_field("registered")
|
71
71
|
else:
|
72
72
|
del self["registered"]
|
73
73
|
|
74
|
-
def set_remove(self, value):
|
74
|
+
def set_remove(self, value) -> None:
|
75
75
|
if value:
|
76
76
|
self.add_field("remove")
|
77
77
|
else:
|
78
78
|
del self["remove"]
|
79
79
|
|
80
|
-
def add_field(self, value):
|
80
|
+
def add_field(self, value: str) -> None:
|
81
81
|
self._set_sub_text(value, "", keep=True)
|
82
82
|
|
83
83
|
def get_fields(self):
|
@@ -87,12 +87,12 @@ class Register(ElementBase):
|
|
87
87
|
fields.add(field)
|
88
88
|
return fields
|
89
89
|
|
90
|
-
def set_fields(self, fields):
|
90
|
+
def set_fields(self, fields) -> None:
|
91
91
|
del self["fields"]
|
92
92
|
for field in fields:
|
93
93
|
self._set_sub_text(field, "", keep=True)
|
94
94
|
|
95
|
-
def del_fields(self):
|
95
|
+
def del_fields(self) -> None:
|
96
96
|
for field in self.form_fields:
|
97
97
|
self._del_sub(field)
|
98
98
|
|
@@ -27,7 +27,7 @@ class XEP_0100(BasePlugin):
|
|
27
27
|
"needs_registration": True,
|
28
28
|
}
|
29
29
|
|
30
|
-
def plugin_init(self):
|
30
|
+
def plugin_init(self) -> None:
|
31
31
|
if not self.xmpp.is_component:
|
32
32
|
log.error("Only components can be gateways, aborting plugin load")
|
33
33
|
return
|
@@ -50,7 +50,7 @@ class XEP_0100(BasePlugin):
|
|
50
50
|
|
51
51
|
register_stanza_plugin(Iq, stanza.Gateway)
|
52
52
|
|
53
|
-
def plugin_end(self):
|
53
|
+
def plugin_end(self) -> None:
|
54
54
|
if not self.xmpp.is_component:
|
55
55
|
self.xmpp.remove_event_handler("user_register", self.on_user_register)
|
56
56
|
self.xmpp.remove_event_handler("user_unregister", self.on_user_unregister)
|
@@ -63,18 +63,16 @@ class XEP_0100(BasePlugin):
|
|
63
63
|
async def get_user(self, stanza):
|
64
64
|
return await self.xmpp["xep_0077"].api["user_get"](None, None, None, stanza)
|
65
65
|
|
66
|
-
async def on_user_unregister(self, iq: Iq):
|
66
|
+
async def on_user_unregister(self, iq: Iq) -> None:
|
67
67
|
self.xmpp.send_presence(pto=iq.get_from().bare, ptype="unavailable")
|
68
68
|
self.xmpp.send_presence(pto=iq.get_from().bare, ptype="unsubscribe")
|
69
69
|
self.xmpp.send_presence(pto=iq.get_from().bare, ptype="unsubscribed")
|
70
70
|
|
71
|
-
async def on_user_register(self, iq: Iq):
|
71
|
+
async def on_user_register(self, iq: Iq) -> None:
|
72
72
|
self.xmpp.client_roster[iq.get_from()].load()
|
73
73
|
await self.add_component_to_roster(jid=iq.get_from())
|
74
74
|
|
75
|
-
async def add_component_to_roster(self, jid: JID):
|
76
|
-
if config.NO_ROSTER_PUSH:
|
77
|
-
return
|
75
|
+
async def add_component_to_roster(self, jid: JID) -> None:
|
78
76
|
items = {
|
79
77
|
self.xmpp.boundjid.bare: {
|
80
78
|
"name": self.component_name,
|
@@ -85,17 +83,18 @@ class XEP_0100(BasePlugin):
|
|
85
83
|
try:
|
86
84
|
await self._set_roster(jid, items)
|
87
85
|
except PermissionError:
|
86
|
+
from slidge import __version__
|
87
|
+
|
88
88
|
warnings.warn(
|
89
|
-
"Slidge does not have the privilege to manage
|
90
|
-
"
|
89
|
+
"Slidge does not have the privilege to manage rosters. See "
|
90
|
+
f"https://slidge.im/docs/slidge/{__version__}/admin/privilege.html"
|
91
91
|
)
|
92
|
-
|
93
|
-
self.xmpp.send_presence(ptype="subscribe", pto=jid.bare)
|
92
|
+
self.xmpp.send_presence(ptype="subscribe", pto=jid.bare)
|
94
93
|
|
95
|
-
async def _set_roster(self, jid, items):
|
94
|
+
async def _set_roster(self, jid, items) -> None:
|
96
95
|
await self.xmpp["xep_0356"].set_roster(jid=jid.bare, roster_items=items)
|
97
96
|
|
98
|
-
def on_presence_unsubscribe(self, p: Presence):
|
97
|
+
def on_presence_unsubscribe(self, p: Presence) -> None:
|
99
98
|
if p.get_to() == self.xmpp.boundjid.bare:
|
100
99
|
log.debug("REMOVE: Our roster: %s", self.xmpp.client_roster)
|
101
100
|
self.xmpp["xep_0077"].api["user_remove"](None, None, p["from"], p)
|
slidge/util/archive_msg.py
CHANGED
@@ -5,10 +5,14 @@ from uuid import uuid4
|
|
5
5
|
from xml.etree import ElementTree as ET
|
6
6
|
|
7
7
|
from slixmpp import Message
|
8
|
-
from slixmpp.plugins.xep_0297 import Forwarded
|
8
|
+
from slixmpp.plugins.xep_0297.stanza import Forwarded
|
9
9
|
|
10
10
|
|
11
|
-
def fix_namespaces(
|
11
|
+
def fix_namespaces(
|
12
|
+
xml: ET.Element,
|
13
|
+
old: str = "{jabber:component:accept}",
|
14
|
+
new: str = "{jabber:client}",
|
15
|
+
) -> None:
|
12
16
|
"""
|
13
17
|
Hack to fix namespaces between jabber:component and jabber:client
|
14
18
|
|
@@ -24,7 +28,9 @@ def fix_namespaces(xml, old="{jabber:component:accept}", new="{jabber:client}"):
|
|
24
28
|
|
25
29
|
|
26
30
|
class HistoryMessage:
|
27
|
-
def __init__(
|
31
|
+
def __init__(
|
32
|
+
self, stanza: Union[Message, str], when: Optional[datetime] = None
|
33
|
+
) -> None:
|
28
34
|
if isinstance(stanza, str):
|
29
35
|
from_db = True
|
30
36
|
stanza = Message(xml=ET.fromstring(stanza))
|
@@ -48,14 +54,14 @@ class HistoryMessage:
|
|
48
54
|
self.stanza: Message = stanza
|
49
55
|
|
50
56
|
@property
|
51
|
-
def stanza_component_ns(self):
|
57
|
+
def stanza_component_ns(self) -> Message:
|
52
58
|
stanza = copy(self.stanza)
|
53
59
|
fix_namespaces(
|
54
60
|
stanza.xml, old="{jabber:client}", new="{jabber:component:accept}"
|
55
61
|
)
|
56
62
|
return stanza
|
57
63
|
|
58
|
-
def forwarded(self):
|
64
|
+
def forwarded(self) -> Forwarded:
|
59
65
|
forwarded = Forwarded()
|
60
66
|
forwarded["delay"]["stamp"] = self.when
|
61
67
|
forwarded.append(self.stanza)
|
slidge/util/conf.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import logging
|
2
2
|
from functools import cached_property
|
3
3
|
from types import GenericAlias
|
4
|
-
from typing import Optional, Union, get_args, get_origin, get_type_hints
|
4
|
+
from typing import Any, Optional, Union, cast, get_args, get_origin, get_type_hints
|
5
5
|
|
6
6
|
import configargparse
|
7
7
|
|
@@ -11,31 +11,31 @@ class Option:
|
|
11
11
|
DYNAMIC_DEFAULT_SUFFIX = "__DYNAMIC_DEFAULT"
|
12
12
|
SHORT_SUFFIX = "__SHORT"
|
13
13
|
|
14
|
-
def __init__(self, parent: "ConfigModule", name: str):
|
14
|
+
def __init__(self, parent: "ConfigModule", name: str) -> None:
|
15
15
|
self.parent = parent
|
16
16
|
self.config_obj = parent.config_obj
|
17
17
|
self.name = name
|
18
18
|
|
19
19
|
@cached_property
|
20
|
-
def doc(self):
|
21
|
-
return getattr(self.config_obj, self.name + self.DOC_SUFFIX)
|
20
|
+
def doc(self) -> str:
|
21
|
+
return getattr(self.config_obj, self.name + self.DOC_SUFFIX) # type:ignore
|
22
22
|
|
23
23
|
@cached_property
|
24
|
-
def required(self):
|
24
|
+
def required(self) -> bool:
|
25
25
|
return not hasattr(
|
26
26
|
self.config_obj, self.name + self.DYNAMIC_DEFAULT_SUFFIX
|
27
27
|
) and not hasattr(self.config_obj, self.name)
|
28
28
|
|
29
29
|
@cached_property
|
30
|
-
def default(self):
|
30
|
+
def default(self) -> Any:
|
31
31
|
return getattr(self.config_obj, self.name, None)
|
32
32
|
|
33
33
|
@cached_property
|
34
|
-
def short(self):
|
34
|
+
def short(self) -> str | None:
|
35
35
|
return getattr(self.config_obj, self.name + self.SHORT_SUFFIX, None)
|
36
36
|
|
37
37
|
@cached_property
|
38
|
-
def nargs(self):
|
38
|
+
def nargs(self) -> str | int | None:
|
39
39
|
type_ = get_type_hints(self.config_obj).get(self.name, type(self.default))
|
40
40
|
|
41
41
|
if isinstance(type_, GenericAlias):
|
@@ -44,9 +44,10 @@ class Option:
|
|
44
44
|
return "*"
|
45
45
|
else:
|
46
46
|
return len(args)
|
47
|
+
return None
|
47
48
|
|
48
49
|
@cached_property
|
49
|
-
def type(self):
|
50
|
+
def type(self) -> Any:
|
50
51
|
type_ = get_type_hints(self.config_obj).get(self.name, type(self.default))
|
51
52
|
|
52
53
|
if _is_optional(type_):
|
@@ -58,14 +59,14 @@ class Option:
|
|
58
59
|
return type_
|
59
60
|
|
60
61
|
@cached_property
|
61
|
-
def names(self):
|
62
|
+
def names(self) -> list[str]:
|
62
63
|
res = ["--" + self.name.lower().replace("_", "-")]
|
63
64
|
if s := self.short:
|
64
65
|
res.append("-" + s)
|
65
66
|
return res
|
66
67
|
|
67
68
|
@cached_property
|
68
|
-
def kwargs(self):
|
69
|
+
def kwargs(self) -> dict[str, Any]:
|
69
70
|
kwargs = dict(
|
70
71
|
required=self.required,
|
71
72
|
help=self.doc,
|
@@ -87,7 +88,7 @@ class Option:
|
|
87
88
|
kwargs["nargs"] = n
|
88
89
|
return kwargs
|
89
90
|
|
90
|
-
def name_to_env_var(self):
|
91
|
+
def name_to_env_var(self) -> str:
|
91
92
|
return self.parent.ENV_VAR_PREFIX + self.name
|
92
93
|
|
93
94
|
|
@@ -96,10 +97,10 @@ class ConfigModule:
|
|
96
97
|
|
97
98
|
def __init__(
|
98
99
|
self,
|
99
|
-
config_obj,
|
100
|
+
config_obj: Any,
|
100
101
|
parser: Optional[configargparse.ArgumentParser] = None,
|
101
102
|
skip_options: tuple[str, ...] = (),
|
102
|
-
):
|
103
|
+
) -> None:
|
103
104
|
self.config_obj = config_obj
|
104
105
|
if parser is None:
|
105
106
|
parser = configargparse.ArgumentParser()
|
@@ -108,7 +109,7 @@ class ConfigModule:
|
|
108
109
|
self.skip_options = skip_options
|
109
110
|
self.add_options_to_parser(skip_options)
|
110
111
|
|
111
|
-
def _list_options(self):
|
112
|
+
def _list_options(self) -> set[str]:
|
112
113
|
return {
|
113
114
|
o
|
114
115
|
for o in (set(dir(self.config_obj)) | set(get_type_hints(self.config_obj)))
|
@@ -118,7 +119,9 @@ class ConfigModule:
|
|
118
119
|
and o.lower() not in self.skip_options
|
119
120
|
}
|
120
121
|
|
121
|
-
def set_conf(
|
122
|
+
def set_conf(
|
123
|
+
self, argv: Optional[list[str]] = None
|
124
|
+
) -> tuple[configargparse.Namespace, list[str]]:
|
122
125
|
if argv is not None:
|
123
126
|
# this is ugly, but necessary because for plugin config, we used
|
124
127
|
# remaining argv.
|
@@ -186,7 +189,7 @@ class ConfigModule:
|
|
186
189
|
res.append(Option(self, opt))
|
187
190
|
return res
|
188
191
|
|
189
|
-
def add_options_to_parser(self, skip_options: tuple[str, ...]):
|
192
|
+
def add_options_to_parser(self, skip_options: tuple[str, ...]) -> None:
|
190
193
|
skip_options = tuple(o.lower() for o in skip_options)
|
191
194
|
p = self.parser
|
192
195
|
for o in sorted(self.options, key=lambda x: (not x.required, x.name)):
|
@@ -194,11 +197,11 @@ class ConfigModule:
|
|
194
197
|
continue
|
195
198
|
p.add_argument(*o.names, **o.kwargs)
|
196
199
|
|
197
|
-
def update_dynamic_defaults(self, args):
|
200
|
+
def update_dynamic_defaults(self, args: configargparse.Namespace) -> None:
|
198
201
|
pass
|
199
202
|
|
200
203
|
|
201
|
-
def _is_optional(t):
|
204
|
+
def _is_optional(t: Any) -> bool:
|
202
205
|
if get_origin(t) is Union:
|
203
206
|
args = get_args(t)
|
204
207
|
if len(args) == 2 and isinstance(None, args[1]):
|
@@ -206,7 +209,7 @@ def _is_optional(t):
|
|
206
209
|
return False
|
207
210
|
|
208
211
|
|
209
|
-
def _argv_to_option_name(arg: str):
|
212
|
+
def _argv_to_option_name(arg: str) -> str:
|
210
213
|
return arg.upper().removeprefix("--").replace("-", "_")
|
211
214
|
|
212
215
|
|