slidge 0.2.12__py3-none-any.whl → 0.3.0a1__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 (77) 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 +119 -209
  8. slidge/contact/roster.py +106 -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 +117 -92
  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 +21 -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 +168 -84
  29. slidge/core/mixins/__init__.py +1 -11
  30. slidge/core/mixins/attachment.py +163 -148
  31. slidge/core/mixins/avatar.py +100 -177
  32. slidge/core/mixins/db.py +50 -2
  33. slidge/core/mixins/message.py +19 -17
  34. slidge/core/mixins/message_maker.py +29 -15
  35. slidge/core/mixins/message_text.py +38 -30
  36. slidge/core/mixins/presence.py +91 -35
  37. slidge/core/pubsub.py +42 -47
  38. slidge/core/session.py +88 -57
  39. slidge/db/alembic/versions/0337c90c0b96_unify_legacy_xmpp_id_mappings.py +183 -0
  40. slidge/db/alembic/versions/4dbd23a3f868_new_avatar_store.py +105 -0
  41. slidge/db/alembic/versions/54ce3cde350c_use_hash_for_avatar_filenames.py +50 -0
  42. slidge/db/alembic/versions/58b98dacf819_refactor.py +118 -0
  43. slidge/db/alembic/versions/75a62b74b239_ditch_hats_table.py +74 -0
  44. slidge/db/avatar.py +150 -119
  45. slidge/db/meta.py +33 -22
  46. slidge/db/models.py +68 -117
  47. slidge/db/store.py +412 -1094
  48. slidge/group/archive.py +61 -54
  49. slidge/group/bookmarks.py +74 -55
  50. slidge/group/participant.py +135 -142
  51. slidge/group/room.py +315 -312
  52. slidge/main.py +28 -18
  53. slidge/migration.py +2 -12
  54. slidge/slixfix/__init__.py +20 -4
  55. slidge/slixfix/delivery_receipt.py +6 -4
  56. slidge/slixfix/link_preview/link_preview.py +1 -1
  57. slidge/slixfix/link_preview/stanza.py +1 -1
  58. slidge/slixfix/roster.py +5 -7
  59. slidge/slixfix/xep_0077/register.py +8 -8
  60. slidge/slixfix/xep_0077/stanza.py +7 -7
  61. slidge/slixfix/xep_0100/gateway.py +12 -13
  62. slidge/slixfix/xep_0153/vcard_avatar.py +1 -1
  63. slidge/slixfix/xep_0292/vcard4.py +1 -1
  64. slidge/util/archive_msg.py +11 -5
  65. slidge/util/conf.py +23 -20
  66. slidge/util/jid_escaping.py +1 -1
  67. slidge/{core/mixins → util}/lock.py +6 -6
  68. slidge/util/test.py +30 -29
  69. slidge/util/types.py +22 -18
  70. slidge/util/util.py +19 -22
  71. {slidge-0.2.12.dist-info → slidge-0.3.0a1.dist-info}/METADATA +1 -1
  72. slidge-0.3.0a1.dist-info/RECORD +117 -0
  73. {slidge-0.2.12.dist-info → slidge-0.3.0a1.dist-info}/WHEEL +1 -1
  74. slidge-0.2.12.dist-info/RECORD +0 -112
  75. {slidge-0.2.12.dist-info → slidge-0.3.0a1.dist-info}/entry_points.txt +0 -0
  76. {slidge-0.2.12.dist-info → slidge-0.3.0a1.dist-info}/licenses/LICENSE +0 -0
  77. {slidge-0.2.12.dist-info → slidge-0.3.0a1.dist-info}/top_level.txt +0 -0
@@ -15,9 +15,10 @@ if TYPE_CHECKING:
15
15
 
16
16
 
17
17
  class RegistrationMixin(DispatcherMixin):
18
- def __init__(self, xmpp: "BaseGateway"):
18
+ __slots__: list[str] = []
19
+
20
+ def __init__(self, xmpp: "BaseGateway") -> None:
19
21
  super().__init__(xmpp)
20
- self.xmpp = xmpp
21
22
  xmpp["xep_0077"].api.register(
22
23
  self.xmpp.make_registration_form, "make_registration_form"
23
24
  )
@@ -32,7 +33,10 @@ class RegistrationMixin(DispatcherMixin):
32
33
  xmpp.add_event_handler("user_unregister", self._on_user_unregister)
33
34
 
34
35
  def get_user(self, jid: JID) -> GatewayUser | None:
35
- return self.xmpp.store.users.get(jid)
36
+ session = self.xmpp.get_session_from_jid(jid)
37
+ if session is None:
38
+ return None
39
+ return session.user
36
40
 
37
41
  async def _user_get(
38
42
  self, _gateway_jid, _node, ifrom: JID, iq: Iq
@@ -41,7 +45,7 @@ class RegistrationMixin(DispatcherMixin):
41
45
  ifrom = iq.get_from()
42
46
  return self.get_user(ifrom)
43
47
 
44
- async def _user_validate(self, _gateway_jid, _node, ifrom: JID, iq: Iq):
48
+ async def _user_validate(self, _gateway_jid, _node, ifrom: JID, iq: Iq) -> None:
45
49
  xmpp = self.xmpp
46
50
  log.debug("User validate: %s", ifrom.bare)
47
51
  form_dict = {f.var: iq.get(f.var) for f in xmpp.REGISTRATION_FIELDS}
@@ -49,10 +53,13 @@ class RegistrationMixin(DispatcherMixin):
49
53
  legacy_module_data = await xmpp.user_prevalidate(ifrom, form_dict)
50
54
  if legacy_module_data is None:
51
55
  legacy_module_data = form_dict
52
- user = self.xmpp.store.users.new(
53
- jid=ifrom,
54
- legacy_module_data=legacy_module_data, # type:ignore
55
- )
56
+ with self.xmpp.store.session() as orm:
57
+ user = GatewayUser(
58
+ jid=ifrom.bare,
59
+ legacy_module_data=legacy_module_data,
60
+ )
61
+ orm.add(user)
62
+ orm.commit()
56
63
  log.info("New user: %s", user)
57
64
 
58
65
  async def _user_modify(
@@ -60,13 +67,14 @@ class RegistrationMixin(DispatcherMixin):
60
67
  ):
61
68
  await self.xmpp.user_prevalidate(ifrom, form_dict)
62
69
  log.debug("Modify user: %s", ifrom)
63
- user = self.xmpp.store.users.get(ifrom)
70
+ with self.xmpp.store.session() as orm:
71
+ user = orm.query(GatewayUser).one_or_none()
64
72
  if user is None:
65
73
  raise XMPPError("internal-server-error", "User not found")
66
74
  user.legacy_module_data.update(form_dict)
67
75
  self.xmpp.store.users.update(user)
68
76
 
69
- async def _on_user_register(self, iq: Iq):
77
+ async def _on_user_register(self, iq: Iq) -> None:
70
78
  session = await self._get_session(iq, wait_for_ready=False)
71
79
  for jid in config.ADMINS:
72
80
  self.xmpp.send_message(
@@ -78,8 +86,8 @@ class RegistrationMixin(DispatcherMixin):
78
86
  session.send_gateway_message(self.xmpp.WELCOME_MESSAGE)
79
87
  await self.xmpp.login_wrap(session)
80
88
 
81
- async def _on_user_unregister(self, iq: Iq):
82
- await self.xmpp.session_cls.kill_by_jid(iq.get_from())
89
+ async def _on_user_unregister(self, iq: Iq) -> None:
90
+ await self.xmpp.kill_session(iq.get_from())
83
91
 
84
92
 
85
93
  log = logging.getLogger(__name__)
@@ -3,6 +3,7 @@ from typing import TYPE_CHECKING
3
3
  from slixmpp import JID, CoroutineCallback, Iq, StanzaPath
4
4
  from slixmpp.exceptions import XMPPError
5
5
 
6
+ from ...db.models import GatewayUser
6
7
  from .util import DispatcherMixin, exceptions_to_xmpp_errors
7
8
 
8
9
  if TYPE_CHECKING:
@@ -10,7 +11,9 @@ if TYPE_CHECKING:
10
11
 
11
12
 
12
13
  class SearchMixin(DispatcherMixin):
13
- def __init__(self, xmpp: "BaseGateway"):
14
+ __slots__: list[str] = []
15
+
16
+ def __init__(self, xmpp: "BaseGateway") -> None:
14
17
  super().__init__(xmpp)
15
18
 
16
19
  xmpp["xep_0055"].api.register(self.search_get_form, "search_get_form")
@@ -21,7 +24,7 @@ class SearchMixin(DispatcherMixin):
21
24
  CoroutineCallback(
22
25
  "iq:gateway",
23
26
  StanzaPath("iq/gateway"),
24
- self._handle_gateway_iq, # type: ignore
27
+ self._handle_gateway_iq,
25
28
  )
26
29
  )
27
30
 
@@ -29,7 +32,8 @@ class SearchMixin(DispatcherMixin):
29
32
  """
30
33
  Prepare the search form using :attr:`.BaseSession.SEARCH_FIELDS`
31
34
  """
32
- user = self.xmpp.store.users.get(ifrom)
35
+ with self.xmpp.store.session() as orm:
36
+ user = orm.query(GatewayUser).one_or_none()
33
37
  if user is None:
34
38
  raise XMPPError(text="Search is only allowed for registered users")
35
39
 
@@ -30,14 +30,16 @@ class SessionDispatcher(
30
30
  SearchMixin,
31
31
  VCardMixin,
32
32
  ):
33
- def __init__(self, xmpp: "BaseGateway"):
33
+ __slots__: list[str] = ["xmpp", "_MamMixin__mam_cleanup_task"]
34
+
35
+ def __init__(self, xmpp: "BaseGateway") -> None:
34
36
  super().__init__(xmpp)
35
37
  xmpp.add_event_handler(
36
38
  "avatar_metadata_publish", self.on_avatar_metadata_publish
37
39
  )
38
40
 
39
41
  @exceptions_to_xmpp_errors
40
- async def on_avatar_metadata_publish(self, m: Message):
42
+ async def on_avatar_metadata_publish(self, m: Message) -> None:
41
43
  session = await self._get_session(m, timeout=None)
42
44
  if not session.user.preferences.get("sync_avatar", False):
43
45
  session.log.debug("User does not want to sync their avatar")
@@ -46,7 +48,7 @@ class SessionDispatcher(
46
48
 
47
49
  await self.on_avatar_metadata_info(session, info)
48
50
 
49
- async def on_avatar_metadata_info(self, session: BaseSession, info: Info):
51
+ async def on_avatar_metadata_info(self, session: BaseSession, info: Info) -> None:
50
52
  hash_ = info["id"]
51
53
 
52
54
  if session.user.avatar_hash == hash_:
@@ -66,7 +68,10 @@ class SessionDispatcher(
66
68
  height = info["height"]
67
69
  width = info["width"]
68
70
  else:
69
- self.xmpp.store.users.set_avatar_hash(session.user_pk, None)
71
+ with self.xmpp.store.session(expire_on_commit=False) as orm:
72
+ session.user.avatar_hash = None
73
+ orm.add(session.user)
74
+ orm.commit()
70
75
  bytes_ = type_ = height = width = hash_ = None
71
76
  try:
72
77
  await session.on_avatar(bytes_, hash_, type_, width, height)
@@ -80,7 +85,10 @@ class SessionDispatcher(
80
85
  f"Something went wrong trying to set your avatar: {e!r}"
81
86
  )
82
87
  else:
83
- self.xmpp.store.users.set_avatar_hash(session.user_pk, hash_)
88
+ session.user.avatar_hash = hash_
89
+ with self.xmpp.store.session(expire_on_commit=False) as orm:
90
+ orm.add(session.user)
91
+ orm.commit()
84
92
  for room in session.bookmarks:
85
93
  participant = await room.get_user_participant()
86
94
  participant.send_last_presence(force=True, no_cache_online=True)
@@ -19,15 +19,17 @@ class Ignore(BaseException):
19
19
 
20
20
 
21
21
  class DispatcherMixin:
22
- def __init__(self, xmpp: "BaseGateway"):
23
- self.xmpp = xmpp
22
+ __slots__: list[str] = []
23
+
24
+ def __init__(self, xmpp: "BaseGateway") -> None:
25
+ self.xmpp = xmpp # type:ignore[misc]
24
26
 
25
27
  async def _get_session(
26
28
  self,
27
29
  stanza: Message | Presence | Iq,
28
30
  timeout: int | None = 10,
29
- wait_for_ready=True,
30
- logged=False,
31
+ wait_for_ready: bool = True,
32
+ logged: bool = False,
31
33
  ) -> BaseSession:
32
34
  xmpp = self.xmpp
33
35
  if stanza.get_from().server == xmpp.boundjid.bare:
@@ -51,8 +53,8 @@ class DispatcherMixin:
51
53
  self,
52
54
  jid: JID,
53
55
  timeout: int | None = 10,
54
- wait_for_ready=True,
55
- logged=False,
56
+ wait_for_ready: bool = True,
57
+ logged: bool = False,
56
58
  ) -> BaseSession:
57
59
  session = self.xmpp.get_session_from_jid(jid)
58
60
  if session is None:
@@ -72,14 +74,15 @@ class DispatcherMixin:
72
74
  muc = await session.bookmarks.by_jid(ito)
73
75
  return muc
74
76
 
75
- def _xmpp_msg_id_to_legacy(self, session: "BaseSession", xmpp_id: str):
76
- sent = self.xmpp.store.sent.get_legacy_id(session.user_pk, xmpp_id)
77
- if sent is not None:
78
- return self.xmpp.LEGACY_MSG_ID_TYPE(sent)
79
-
80
- multi = self.xmpp.store.multi.get_legacy_id(session.user_pk, xmpp_id)
81
- if multi:
82
- return self.xmpp.LEGACY_MSG_ID_TYPE(multi)
77
+ def _xmpp_msg_id_to_legacy(
78
+ self, session: "BaseSession", xmpp_id: str, recipient: Recipient
79
+ ):
80
+ with self.xmpp.store.session() as orm:
81
+ sent = self.xmpp.store.id_map.get_legacy(
82
+ orm, recipient.stored.id, xmpp_id, False
83
+ )
84
+ if sent is not None:
85
+ return self.xmpp.LEGACY_MSG_ID_TYPE(sent)
83
86
 
84
87
  try:
85
88
  return session.xmpp_to_legacy_msg_id(xmpp_id)
@@ -91,11 +94,11 @@ class DispatcherMixin:
91
94
  "internal-server-error", "Couldn't convert xmpp msg ID to legacy ID."
92
95
  )
93
96
 
94
- async def _get_session_entity_thread(
97
+ async def _get_session_recipient_thread(
95
98
  self, msg: Message
96
99
  ) -> tuple["BaseSession", Recipient, int | str]:
97
100
  session = await self._get_session(msg)
98
- e: Recipient = await _get_entity(session, msg)
101
+ e: Recipient = await get_recipient(session, msg)
99
102
  legacy_thread = await self._xmpp_to_legacy_thread(session, msg, e)
100
103
  return session, e, legacy_thread
101
104
 
@@ -107,22 +110,29 @@ class DispatcherMixin:
107
110
  return None
108
111
 
109
112
  if session.MESSAGE_IDS_ARE_THREAD_IDS:
110
- return self._xmpp_msg_id_to_legacy(session, xmpp_thread)
113
+ return self._xmpp_msg_id_to_legacy(session, xmpp_thread, recipient)
111
114
 
112
- legacy_thread_str = session.xmpp.store.sent.get_legacy_thread(
113
- session.user_pk, xmpp_thread
114
- )
115
- if legacy_thread_str is not None:
116
- return session.xmpp.LEGACY_MSG_ID_TYPE(legacy_thread_str)
115
+ with session.xmpp.store.session() as orm:
116
+ legacy_thread_str = session.xmpp.store.id_map.get_thread(
117
+ orm, recipient.stored.id, xmpp_thread, recipient.is_group
118
+ )
119
+ if legacy_thread_str is not None:
120
+ return session.xmpp.LEGACY_MSG_ID_TYPE(legacy_thread_str)
117
121
  async with session.thread_creation_lock:
118
122
  legacy_thread = await recipient.create_thread(xmpp_thread)
119
- session.xmpp.store.sent.set_thread(
120
- session.user_pk, str(legacy_thread), xmpp_thread
121
- )
123
+ with session.xmpp.store.session() as orm:
124
+ session.xmpp.store.id_map.set_thread(
125
+ orm,
126
+ recipient.stored.id,
127
+ str(legacy_thread),
128
+ xmpp_thread,
129
+ recipient.is_group,
130
+ )
131
+ orm.commit()
122
132
  return legacy_thread
123
133
 
124
134
 
125
- def _ignore(session: "BaseSession", msg: Message):
135
+ def _ignore(session: "BaseSession", msg: Message) -> bool:
126
136
  i = msg.get_id()
127
137
  if i.startswith("slidge-carbon-"):
128
138
  return True
@@ -133,7 +143,7 @@ def _ignore(session: "BaseSession", msg: Message):
133
143
  return True
134
144
 
135
145
 
136
- async def _get_entity(session: "BaseSession", m: Message) -> RecipientType:
146
+ async def get_recipient(session: "BaseSession", m: Message) -> RecipientType:
137
147
  session.raise_if_not_logged()
138
148
  if m.get_type() == "groupchat":
139
149
  muc = await session.bookmarks.by_jid(m.get_to())
@@ -8,11 +8,14 @@ from slixmpp.plugins.xep_0292.stanza import NS as VCard4NS
8
8
  from ...contact import LegacyContact
9
9
  from ...core.session import BaseSession
10
10
  from ...group import LegacyParticipant
11
+ from ...util.types import Avatar
11
12
  from .util import DispatcherMixin, exceptions_to_xmpp_errors
12
13
 
13
14
 
14
15
  class VCardMixin(DispatcherMixin):
15
- def __init__(self, xmpp):
16
+ __slots__: list[str] = []
17
+
18
+ def __init__(self, xmpp) -> None:
16
19
  super().__init__(xmpp)
17
20
  xmpp.register_handler(
18
21
  CoroutineCallback(
@@ -34,7 +37,7 @@ class VCardMixin(DispatcherMixin):
34
37
  )
35
38
 
36
39
  @exceptions_to_xmpp_errors
37
- async def on_get_vcard(self, iq: Iq):
40
+ async def on_get_vcard(self, iq: Iq) -> None:
38
41
  session = await self._get_session(iq, logged=True)
39
42
  contact = await session.contacts.by_jid(iq.get_to())
40
43
  vcard = await contact.get_vcard()
@@ -139,8 +142,8 @@ class VCardMixin(DispatcherMixin):
139
142
  reply.send()
140
143
 
141
144
  if not data:
142
- await muc.set_avatar(None, blocking=True)
145
+ await muc.set_avatar(None)
143
146
  return
144
147
 
145
148
  if legacy_id:
146
- await muc.set_avatar(data, legacy_id, blocking=True)
149
+ await muc.set_avatar(Avatar(data=data, unique_id=legacy_id))