slidge 0.2.11__py3-none-any.whl → 0.3.0__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.
Files changed (93) hide show
  1. slidge/__init__.py +5 -2
  2. slidge/command/adhoc.py +9 -3
  3. slidge/command/admin.py +16 -12
  4. slidge/command/base.py +16 -12
  5. slidge/command/chat_command.py +25 -16
  6. slidge/command/user.py +7 -8
  7. slidge/contact/contact.py +123 -210
  8. slidge/contact/roster.py +108 -105
  9. slidge/core/config.py +2 -43
  10. slidge/core/dispatcher/caps.py +9 -2
  11. slidge/core/dispatcher/disco.py +13 -3
  12. slidge/core/dispatcher/message/__init__.py +1 -1
  13. slidge/core/dispatcher/message/chat_state.py +17 -8
  14. slidge/core/dispatcher/message/marker.py +7 -5
  15. slidge/core/dispatcher/message/message.py +120 -93
  16. slidge/core/dispatcher/muc/__init__.py +1 -1
  17. slidge/core/dispatcher/muc/admin.py +4 -4
  18. slidge/core/dispatcher/muc/mam.py +10 -6
  19. slidge/core/dispatcher/muc/misc.py +4 -2
  20. slidge/core/dispatcher/muc/owner.py +5 -3
  21. slidge/core/dispatcher/muc/ping.py +3 -1
  22. slidge/core/dispatcher/presence.py +26 -15
  23. slidge/core/dispatcher/registration.py +20 -12
  24. slidge/core/dispatcher/search.py +7 -3
  25. slidge/core/dispatcher/session_dispatcher.py +13 -5
  26. slidge/core/dispatcher/util.py +37 -27
  27. slidge/core/dispatcher/vcard.py +7 -4
  28. slidge/core/gateway.py +177 -87
  29. slidge/core/mixins/__init__.py +1 -11
  30. slidge/core/mixins/attachment.py +200 -147
  31. slidge/core/mixins/avatar.py +105 -177
  32. slidge/core/mixins/base.py +3 -1
  33. slidge/core/mixins/db.py +50 -2
  34. slidge/core/mixins/disco.py +1 -1
  35. slidge/core/mixins/message.py +19 -17
  36. slidge/core/mixins/message_maker.py +29 -15
  37. slidge/core/mixins/message_text.py +67 -30
  38. slidge/core/mixins/presence.py +94 -37
  39. slidge/core/pubsub.py +42 -47
  40. slidge/core/session.py +95 -60
  41. slidge/db/alembic/versions/cef02a8b1451_initial_schema.py +361 -0
  42. slidge/db/avatar.py +150 -119
  43. slidge/db/meta.py +33 -22
  44. slidge/db/models.py +69 -117
  45. slidge/db/store.py +414 -1094
  46. slidge/group/archive.py +65 -55
  47. slidge/group/bookmarks.py +96 -59
  48. slidge/group/participant.py +150 -144
  49. slidge/group/room.py +351 -328
  50. slidge/main.py +34 -22
  51. slidge/migration.py +17 -29
  52. slidge/slixfix/__init__.py +20 -4
  53. slidge/slixfix/delivery_receipt.py +6 -4
  54. slidge/slixfix/link_preview/link_preview.py +1 -1
  55. slidge/slixfix/link_preview/stanza.py +1 -1
  56. slidge/slixfix/roster.py +5 -7
  57. slidge/slixfix/xep_0077/register.py +8 -8
  58. slidge/slixfix/xep_0077/stanza.py +7 -7
  59. slidge/slixfix/xep_0100/gateway.py +12 -13
  60. slidge/slixfix/xep_0153/vcard_avatar.py +1 -1
  61. slidge/slixfix/xep_0292/vcard4.py +12 -2
  62. slidge/util/archive_msg.py +11 -5
  63. slidge/util/conf.py +27 -21
  64. slidge/util/jid_escaping.py +1 -1
  65. slidge/{core/mixins → util}/lock.py +6 -6
  66. slidge/util/test.py +30 -29
  67. slidge/util/types.py +24 -18
  68. slidge/util/util.py +26 -22
  69. {slidge-0.2.11.dist-info → slidge-0.3.0.dist-info}/METADATA +1 -1
  70. slidge-0.3.0.dist-info/RECORD +95 -0
  71. {slidge-0.2.11.dist-info → slidge-0.3.0.dist-info}/WHEEL +1 -1
  72. slidge/db/alembic/versions/04cf35e3cf85_add_participant_nickname_no_illegal.py +0 -33
  73. slidge/db/alembic/versions/09f27f098baa_add_missing_attributes_in_room.py +0 -36
  74. slidge/db/alembic/versions/15b0bd83407a_remove_bogus_unique_constraints_on_room_.py +0 -85
  75. slidge/db/alembic/versions/2461390c0af2_store_contacts_caps_verstring_in_db.py +0 -36
  76. slidge/db/alembic/versions/29f5280c61aa_store_subject_setter_in_room.py +0 -37
  77. slidge/db/alembic/versions/2b1f45ab7379_store_room_subject_setter_by_nickname.py +0 -41
  78. slidge/db/alembic/versions/3071e0fa69d4_add_contact_client_type.py +0 -52
  79. slidge/db/alembic/versions/45c24cc73c91_add_bob.py +0 -42
  80. slidge/db/alembic/versions/5bd48bfdffa2_lift_room_legacy_id_constraint.py +0 -61
  81. slidge/db/alembic/versions/82a4af84b679_add_muc_history_filled.py +0 -48
  82. slidge/db/alembic/versions/8b993243a536_add_vcard_content_to_contact_table.py +0 -43
  83. slidge/db/alembic/versions/8d2ced764698_rely_on_db_to_store_contacts_rooms_and_.py +0 -139
  84. slidge/db/alembic/versions/aa9d82a7f6ef_db_creation.py +0 -50
  85. slidge/db/alembic/versions/abba1ae0edb3_store_avatar_legacy_id_in_the_contact_.py +0 -79
  86. slidge/db/alembic/versions/b33993e87db3_move_everything_to_persistent_db.py +0 -214
  87. slidge/db/alembic/versions/b64b1a793483_add_source_and_legacy_id_for_archived_.py +0 -52
  88. slidge/db/alembic/versions/c4a8ec35a0e8_per_room_user_nick.py +0 -34
  89. slidge/db/alembic/versions/e91195719c2c_store_users_avatars_persistently.py +0 -26
  90. slidge-0.2.11.dist-info/RECORD +0 -112
  91. {slidge-0.2.11.dist-info → slidge-0.3.0.dist-info}/entry_points.txt +0 -0
  92. {slidge-0.2.11.dist-info → slidge-0.3.0.dist-info}/licenses/LICENSE +0 -0
  93. {slidge-0.2.11.dist-info → slidge-0.3.0.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
- logging.basicConfig(
43
- level=args.loglevel,
44
- filename=args.log_file,
45
- force=True,
46
- format=args.log_format,
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
 
@@ -157,22 +166,25 @@ def main(module_name: str | None = None) -> None:
157
166
  f"_{config.LEGACY_MODULE.split('.')[-1].upper()}_"
158
167
  )
159
168
  logging.debug("Env var prefix: %s", ConfigModule.ENV_VAR_PREFIX)
160
- ConfigModule(plugin_config_obj).set_conf(unknown_argv)
161
- else:
162
- if unknown_argv:
163
- raise RuntimeError("Some arguments have not been recognized", unknown_argv)
169
+ _, unknown_argv = ConfigModule(plugin_config_obj).set_conf(unknown_argv)
170
+
171
+ if unknown_argv:
172
+ logging.error(
173
+ f"These config options have not been recognized and ignored: {unknown_argv}"
174
+ )
164
175
 
165
176
  migrate()
166
177
 
167
- store = SlidgeStore(get_engine(config.DB_URL))
178
+ store = SlidgeStore(
179
+ get_engine(
180
+ config.DB_URL, echo=logging.getLogger().isEnabledFor(level=logging.DEBUG)
181
+ )
182
+ )
168
183
  BaseGateway.store = store
169
184
  gateway: BaseGateway = BaseGateway.get_unique_subclass()()
170
185
  avatar_cache.store = gateway.store.avatars
171
186
  avatar_cache.set_dir(config.HOME_DIR / "slidge_avatars_v3")
172
187
 
173
- PepAvatar.store = gateway.store
174
- PepNick.contact_store = gateway.store.contacts
175
-
176
188
  gateway.connect()
177
189
 
178
190
  return_code = 0
slidge/migration.py CHANGED
@@ -1,23 +1,10 @@
1
1
  import logging
2
- import shutil
3
2
  import sys
3
+ import traceback
4
4
  from pathlib import Path
5
5
 
6
6
  from alembic import command
7
7
  from alembic.config import Config
8
- from slixmpp import JID
9
-
10
- from .core import config
11
- from .db.meta import get_engine
12
- from .db.models import GatewayUser
13
- from .db.store import SlidgeStore
14
-
15
-
16
- def remove_avatar_cache_v1():
17
- old_dir = config.HOME_DIR / "slidge_avatars"
18
- if old_dir.exists():
19
- log.info("Avatar cache dir v1 found, clearing it.")
20
- shutil.rmtree(old_dir)
21
8
 
22
9
 
23
10
  def get_alembic_cfg() -> Config:
@@ -30,27 +17,28 @@ def get_alembic_cfg() -> Config:
30
17
  return alembic_cfg
31
18
 
32
19
 
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
20
  def migrate() -> None:
43
- remove_avatar_cache_v1()
44
- command.upgrade(get_alembic_cfg(), "head")
45
- remove_resource_parts_from_users()
46
-
47
-
48
- def main():
21
+ try:
22
+ command.upgrade(get_alembic_cfg(), "head")
23
+ except Exception as e:
24
+ traceback.print_exception(e)
25
+ print(
26
+ "Something went wrong during the migration. "
27
+ "This is expected if you upgrade from slidge 0.2, in this case you need to start from a fresh database."
28
+ )
29
+ exit(1)
30
+
31
+
32
+ def main() -> None:
49
33
  """
50
34
  Updates the (dev) database in ./dev/slidge.sqlite and generates a revision
51
35
 
52
36
  Usage: python -m slidge.migration "Revision message blah blah blah"
53
37
  """
38
+ dev_db = Path(".") / "dev" / "slidge.sqlite"
39
+ if dev_db.exists():
40
+ # always start from a clean state
41
+ dev_db.unlink()
54
42
  alembic_cfg = get_alembic_cfg()
55
43
  command.upgrade(alembic_cfg, "head")
56
44
  command.revision(alembic_cfg, sys.argv[1], autogenerate=True)
@@ -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 set_pinned(self, val: bool):
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(self, msg_id: str, mfrom: JID, mto: JID, mtype: MessageTypes = "chat"):
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
 
@@ -13,5 +13,5 @@ class LinkPreview(BasePlugin):
13
13
  dependencies = set()
14
14
  stanza = stanza
15
15
 
16
- def plugin_init(self):
16
+ def plugin_init(self) -> None:
17
17
  stanza.register_plugin()
@@ -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
- log.debug("Load %s", jid)
43
- user = self.xmpp.store.users.get(JID(jid))
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 users' rosters. "
90
- "Users should add the slidge component to their rosters manually."
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
- if config.ROSTER_PUSH_PRESENCE_SUBSCRIPTION_REQUEST_FALLBACK:
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)
@@ -10,5 +10,5 @@ class XEP_0153(BasePlugin):
10
10
  dependencies = {"xep_0054"}
11
11
  stanza = stanza
12
12
 
13
- def plugin_init(self):
13
+ def plugin_init(self) -> None:
14
14
  register_stanza_plugin(Presence, VCardTempUpdate)
@@ -1,5 +1,6 @@
1
+ from slixmpp import register_stanza_plugin, __version_info__
1
2
  from slixmpp.plugins.base import BasePlugin, register_plugin
2
- from slixmpp.plugins.xep_0292.stanza import NS
3
+ from slixmpp.plugins.xep_0292.stanza import NS, _VCardTextElementBase, VCard4
3
4
 
4
5
 
5
6
  class VCard4Provider(BasePlugin):
@@ -7,8 +8,17 @@ class VCard4Provider(BasePlugin):
7
8
  description = "VCard4 Provider"
8
9
  dependencies = {"xep_0030"}
9
10
 
10
- def plugin_init(self):
11
+ def plugin_init(self) -> None:
11
12
  self.xmpp.plugin["xep_0030"].add_feature(NS)
12
13
 
13
14
 
15
+
16
+
14
17
  register_plugin(VCard4Provider)
18
+
19
+
20
+ if __version_info__[0] <= 1 and __version_info__[1] <= 11:
21
+ class Pronouns(_VCardTextElementBase):
22
+ name = plugin_attrib = "pronouns"
23
+
24
+ register_stanza_plugin(VCard4, Pronouns)
@@ -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(xml, old="{jabber:component:accept}", new="{jabber:client}"):
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__(self, stanza: Union[Message, str], when: Optional[datetime] = None):
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)