slidge 0.1.3__py3-none-any.whl → 0.2.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. slidge/__init__.py +3 -5
  2. slidge/__main__.py +2 -197
  3. slidge/__version__.py +5 -0
  4. slidge/command/adhoc.py +40 -17
  5. slidge/command/admin.py +24 -12
  6. slidge/command/base.py +10 -8
  7. slidge/command/categories.py +13 -3
  8. slidge/command/chat_command.py +29 -2
  9. slidge/command/register.py +32 -16
  10. slidge/command/user.py +106 -13
  11. slidge/contact/contact.py +254 -50
  12. slidge/contact/roster.py +124 -53
  13. slidge/core/config.py +19 -13
  14. slidge/core/dispatcher/__init__.py +3 -0
  15. slidge/core/{gateway → dispatcher}/caps.py +12 -8
  16. slidge/core/{gateway → dispatcher}/disco.py +10 -18
  17. slidge/core/dispatcher/message/__init__.py +10 -0
  18. slidge/core/dispatcher/message/chat_state.py +40 -0
  19. slidge/core/dispatcher/message/marker.py +62 -0
  20. slidge/core/dispatcher/message/message.py +397 -0
  21. slidge/core/dispatcher/muc/__init__.py +12 -0
  22. slidge/core/dispatcher/muc/admin.py +98 -0
  23. slidge/core/{gateway → dispatcher/muc}/mam.py +25 -17
  24. slidge/core/dispatcher/muc/misc.py +121 -0
  25. slidge/core/dispatcher/muc/owner.py +96 -0
  26. slidge/core/{gateway → dispatcher/muc}/ping.py +11 -17
  27. slidge/core/dispatcher/presence.py +176 -0
  28. slidge/core/dispatcher/registration.py +85 -0
  29. slidge/core/{gateway → dispatcher}/search.py +9 -16
  30. slidge/core/dispatcher/session_dispatcher.py +84 -0
  31. slidge/core/dispatcher/util.py +174 -0
  32. slidge/core/{gateway/vcard_temp.py → dispatcher/vcard.py} +35 -19
  33. slidge/core/{gateway/base.py → gateway.py} +176 -153
  34. slidge/core/mixins/__init__.py +11 -1
  35. slidge/core/mixins/attachment.py +106 -67
  36. slidge/core/mixins/avatar.py +94 -25
  37. slidge/core/mixins/base.py +10 -4
  38. slidge/core/mixins/db.py +18 -0
  39. slidge/core/mixins/disco.py +0 -10
  40. slidge/core/mixins/lock.py +10 -8
  41. slidge/core/mixins/message.py +11 -195
  42. slidge/core/mixins/message_maker.py +17 -9
  43. slidge/core/mixins/message_text.py +211 -0
  44. slidge/core/mixins/presence.py +17 -4
  45. slidge/core/pubsub.py +114 -288
  46. slidge/core/session.py +101 -40
  47. slidge/db/__init__.py +4 -0
  48. slidge/db/alembic/__init__.py +0 -0
  49. slidge/db/alembic/env.py +64 -0
  50. slidge/db/alembic/old_user_store.py +183 -0
  51. slidge/db/alembic/script.py.mako +26 -0
  52. slidge/db/alembic/versions/09f27f098baa_add_missing_attributes_in_room.py +36 -0
  53. slidge/db/alembic/versions/15b0bd83407a_remove_bogus_unique_constraints_on_room_.py +85 -0
  54. slidge/db/alembic/versions/2461390c0af2_store_contacts_caps_verstring_in_db.py +36 -0
  55. slidge/db/alembic/versions/29f5280c61aa_store_subject_setter_in_room.py +37 -0
  56. slidge/db/alembic/versions/2b1f45ab7379_store_room_subject_setter_by_nickname.py +41 -0
  57. slidge/db/alembic/versions/3071e0fa69d4_add_contact_client_type.py +52 -0
  58. slidge/db/alembic/versions/45c24cc73c91_add_bob.py +42 -0
  59. slidge/db/alembic/versions/5bd48bfdffa2_lift_room_legacy_id_constraint.py +61 -0
  60. slidge/db/alembic/versions/82a4af84b679_add_muc_history_filled.py +48 -0
  61. slidge/db/alembic/versions/8b993243a536_add_vcard_content_to_contact_table.py +43 -0
  62. slidge/db/alembic/versions/8d2ced764698_rely_on_db_to_store_contacts_rooms_and_.py +139 -0
  63. slidge/db/alembic/versions/aa9d82a7f6ef_db_creation.py +101 -0
  64. slidge/db/alembic/versions/abba1ae0edb3_store_avatar_legacy_id_in_the_contact_.py +79 -0
  65. slidge/db/alembic/versions/b33993e87db3_move_everything_to_persistent_db.py +214 -0
  66. slidge/db/alembic/versions/b64b1a793483_add_source_and_legacy_id_for_archived_.py +52 -0
  67. slidge/db/alembic/versions/c4a8ec35a0e8_per_room_user_nick.py +34 -0
  68. slidge/db/alembic/versions/e91195719c2c_store_users_avatars_persistently.py +26 -0
  69. slidge/db/avatar.py +205 -0
  70. slidge/db/meta.py +72 -0
  71. slidge/db/models.py +405 -0
  72. slidge/db/store.py +1257 -0
  73. slidge/group/archive.py +58 -14
  74. slidge/group/bookmarks.py +89 -65
  75. slidge/group/participant.py +107 -40
  76. slidge/group/room.py +402 -213
  77. slidge/main.py +202 -0
  78. slidge/migration.py +45 -1
  79. slidge/slixfix/__init__.py +31 -1
  80. slidge/{core/gateway → slixfix}/delivery_receipt.py +1 -1
  81. slidge/slixfix/roster.py +13 -4
  82. slidge/slixfix/xep_0292/vcard4.py +1 -87
  83. slidge/util/archive_msg.py +2 -1
  84. slidge/util/db.py +4 -228
  85. slidge/util/test.py +91 -4
  86. slidge/util/types.py +39 -4
  87. slidge/util/util.py +45 -2
  88. {slidge-0.1.3.dist-info → slidge-0.2.0.dist-info}/METADATA +10 -5
  89. slidge-0.2.0.dist-info/RECORD +131 -0
  90. slidge-0.2.0.dist-info/entry_points.txt +3 -0
  91. slidge/core/cache.py +0 -183
  92. slidge/core/gateway/__init__.py +0 -3
  93. slidge/core/gateway/muc_admin.py +0 -35
  94. slidge/core/gateway/presence.py +0 -95
  95. slidge/core/gateway/registration.py +0 -53
  96. slidge/core/gateway/session_dispatcher.py +0 -804
  97. slidge/util/schema.sql +0 -126
  98. slidge/util/sql.py +0 -508
  99. slidge-0.1.3.dist-info/RECORD +0 -96
  100. slidge-0.1.3.dist-info/entry_points.txt +0 -3
  101. {slidge-0.1.3.dist-info → slidge-0.2.0.dist-info}/LICENSE +0 -0
  102. {slidge-0.1.3.dist-info → slidge-0.2.0.dist-info}/WHEEL +0 -0
slidge/core/config.py CHANGED
@@ -43,11 +43,18 @@ PORT__SHORT = "p"
43
43
 
44
44
  HOME_DIR: Path
45
45
  HOME_DIR__DOC = (
46
- "Shelve file used to store persistent user data. "
46
+ "Directory where slidge will writes it persistent data and cache. "
47
47
  "Defaults to /var/lib/slidge/${SLIDGE_JID}. "
48
48
  )
49
49
  HOME_DIR__DYNAMIC_DEFAULT = True
50
50
 
51
+ DB_URL: str
52
+ DB_URL__DOC = (
53
+ "Database URL, see <https://docs.sqlalchemy.org/en/20/core/engines.html#database-urls>. "
54
+ "Defaults to sqlite:///${HOME_DIR}/slidge.sqlite"
55
+ )
56
+ DB_URL__DYNAMIC_DEFAULT = True
57
+
51
58
  USER_JID_VALIDATOR: str
52
59
  USER_JID_VALIDATOR__DOC = (
53
60
  "Regular expression to restrict users that can register to the gateway, by JID. "
@@ -70,7 +77,7 @@ UPLOAD_SERVICE__DOC = (
70
77
  )
71
78
 
72
79
  SECRET_KEY: Optional[str] = None
73
- SECRET_KEY__DOC = "Encryption for disk storage"
80
+ SECRET_KEY__DOC = "Encryption for disk storage. Deprecated."
74
81
 
75
82
  NO_ROSTER_PUSH = False
76
83
  NO_ROSTER_PUSH__DOC = "Do not fill users' rosters with legacy contacts automatically"
@@ -135,19 +142,18 @@ PARTIAL_REGISTRATION_TIMEOUT__DOC = (
135
142
  "a single step registration process is not enough."
136
143
  )
137
144
 
138
- LAST_SEEN_FALLBACK = True
145
+ LAST_SEEN_FALLBACK = False
139
146
  LAST_SEEN_FALLBACK__DOC = (
140
147
  "When using XEP-0319 (Last User Interaction in Presence), use the presence status"
141
148
  " to display the last seen information in the presence status. Useful for clients"
142
- " that do not implement XEP-0319."
149
+ " that do not implement XEP-0319. Because of implementation details, this can increase"
150
+ " RAM usage and might be deprecated in the future. Ask your client dev for XEP-0319"
151
+ " support ;o)."
143
152
  )
144
153
 
145
154
  QR_TIMEOUT = 60
146
155
  QR_TIMEOUT__DOC = "Timeout for QR code flashing confirmation."
147
156
 
148
- DOWNLOAD_CHUNK_SIZE = 1024
149
- DOWNLOAD_CHUNK_SIZE__DOC = "Chunk size when slidge needs to download files using HTTP."
150
-
151
157
  LAST_MESSAGE_CORRECTION_RETRACTION_WORKAROUND = False
152
158
  LAST_MESSAGE_CORRECTION_RETRACTION_WORKAROUND__DOC = (
153
159
  "If the legacy service does not support last message correction but supports"
@@ -176,10 +182,7 @@ LOG_FORMAT__DOC = (
176
182
  )
177
183
 
178
184
  MAM_MAX_DAYS = 7
179
- MAM_MAX_DAYS__DOC = (
180
- "Maximum number of days for group archive retention. "
181
- "Since all text content stored in RAM right now, "
182
- )
185
+ MAM_MAX_DAYS__DOC = "Maximum number of days for group archive retention."
183
186
 
184
187
  CORRECTION_EMPTY_BODY_AS_RETRACTION = True
185
188
  CORRECTION_EMPTY_BODY_AS_RETRACTION__DOC = (
@@ -212,5 +215,8 @@ DEV_MODE__DOC = (
212
215
  "Not safe to use in prod, but great during dev."
213
216
  )
214
217
 
215
- SYNC_AVATAR = True
216
- SYNC_AVATAR__DOC = "Sync the user XMPP avatar to legacy network (if supported)."
218
+ STRIP_LEADING_EMOJI_ADHOC = False
219
+ STRIP_LEADING_EMOJI_ADHOC__DOC = (
220
+ "Strip the leading emoji in ad-hoc command names, if present, in case you "
221
+ "are a emoji-hater."
222
+ )
@@ -0,0 +1,3 @@
1
+ from .session_dispatcher import SessionDispatcher
2
+
3
+ __all__ = ("SessionDispatcher",)
@@ -5,19 +5,19 @@ from slixmpp import Presence
5
5
  from slixmpp.exceptions import XMPPError
6
6
  from slixmpp.xmlstream import StanzaBase
7
7
 
8
- from ...contact import LegacyContact
8
+ from .util import DispatcherMixin
9
9
 
10
10
  if TYPE_CHECKING:
11
- from .base import BaseGateway
11
+ from slidge.core.gateway import BaseGateway
12
12
 
13
13
 
14
- class Caps:
14
+ class CapsMixin(DispatcherMixin):
15
15
  def __init__(self, xmpp: "BaseGateway"):
16
- self.xmpp = xmpp
16
+ super().__init__(xmpp)
17
17
  xmpp.del_filter("out", xmpp.plugin["xep_0115"]._filter_add_caps)
18
18
  xmpp.add_filter("out", self._filter_add_caps) # type:ignore
19
19
 
20
- async def _filter_add_caps(self, stanza: StanzaBase):
20
+ async def _filter_add_caps(self, stanza: StanzaBase) -> StanzaBase:
21
21
  # we rolled our own "add caps on presences" filter because
22
22
  # there is too much magic happening in slixmpp
23
23
  # anyway, we probably want to roll our own "dynamic disco"/caps
@@ -25,6 +25,9 @@ class Caps:
25
25
  if not isinstance(stanza, Presence):
26
26
  return stanza
27
27
 
28
+ if stanza.get_plugin("caps", check=True):
29
+ return stanza
30
+
28
31
  if stanza["type"] not in ("available", "chat", "away", "dnd", "xa"):
29
32
  return stanza
30
33
 
@@ -44,10 +47,11 @@ class Caps:
44
47
 
45
48
  await session.ready
46
49
 
47
- entity = await session.get_contact_or_group_or_participant(pfrom)
48
- if not isinstance(entity, LegacyContact):
50
+ try:
51
+ contact = await session.contacts.by_jid(pfrom)
52
+ except XMPPError:
49
53
  return stanza
50
- ver = await entity.get_caps_ver(pfrom)
54
+ ver = await contact.get_caps_ver(pfrom)
51
55
  else:
52
56
  ver = await caps.get_verstring(pfrom)
53
57
 
@@ -5,15 +5,15 @@ from slixmpp.exceptions import XMPPError
5
5
  from slixmpp.plugins.xep_0030.stanza.items import DiscoItems
6
6
  from slixmpp.types import OptJid
7
7
 
8
- from ...util.db import user_store
8
+ from .util import DispatcherMixin
9
9
 
10
10
  if TYPE_CHECKING:
11
- from .base import BaseGateway
11
+ from slidge.core.gateway import BaseGateway
12
12
 
13
13
 
14
- class Disco:
14
+ class DiscoMixin(DispatcherMixin):
15
15
  def __init__(self, xmpp: "BaseGateway"):
16
- self.xmpp = xmpp
16
+ super().__init__(xmpp)
17
17
 
18
18
  xmpp.plugin["xep_0030"].set_node_handler(
19
19
  "get_info",
@@ -38,15 +38,11 @@ class Disco:
38
38
  if ifrom is None:
39
39
  raise XMPPError("subscription-required")
40
40
 
41
- user = user_store.get_by_jid(ifrom)
42
- if user is None:
43
- raise XMPPError("registration-required")
44
- session = self.xmpp.get_session_from_user(user)
45
- await session.wait_for_ready()
41
+ assert jid is not None
42
+ session = await self._get_session_from_jid(jid=ifrom)
46
43
 
47
44
  log.debug("Looking for entity: %s", jid)
48
45
 
49
- assert jid is not None
50
46
  entity = await session.get_contact_or_group_or_participant(jid)
51
47
 
52
48
  if entity is None:
@@ -63,16 +59,12 @@ class Disco:
63
59
  if jid != self.xmpp.boundjid.bare:
64
60
  return DiscoItems()
65
61
 
66
- user = user_store.get_by_jid(ifrom)
67
- if user is None:
68
- raise XMPPError("registration-required")
69
-
70
- session = self.xmpp.get_session_from_user(user)
71
- await session.wait_for_ready()
62
+ assert ifrom is not None
63
+ session = await self._get_session_from_jid(ifrom)
72
64
 
73
65
  d = DiscoItems()
74
- for muc in sorted(session.bookmarks, key=lambda m: m.name):
75
- d.add_item(muc.jid, name=muc.name)
66
+ for room in self.xmpp.store.rooms.get_all_jid_and_names(session.user_pk):
67
+ d.add_item(room.jid, name=room.name)
76
68
 
77
69
  return d
78
70
 
@@ -0,0 +1,10 @@
1
+ from .chat_state import ChatStateMixin
2
+ from .marker import MarkerMixin
3
+ from .message import MessageContentMixin
4
+
5
+
6
+ class MessageMixin(ChatStateMixin, MarkerMixin, MessageContentMixin):
7
+ pass
8
+
9
+
10
+ __all__ = ("MessageMixin",)
@@ -0,0 +1,40 @@
1
+ from slixmpp import Message
2
+ from slixmpp.xmlstream import StanzaBase
3
+
4
+ from ..util import DispatcherMixin, exceptions_to_xmpp_errors
5
+
6
+
7
+ class ChatStateMixin(DispatcherMixin):
8
+ def __init__(self, xmpp) -> None:
9
+ super().__init__(xmpp)
10
+ xmpp.add_event_handler("chatstate_active", self.on_chatstate_active)
11
+ xmpp.add_event_handler("chatstate_inactive", self.on_chatstate_inactive)
12
+ xmpp.add_event_handler("chatstate_composing", self.on_chatstate_composing)
13
+ xmpp.add_event_handler("chatstate_paused", self.on_chatstate_paused)
14
+
15
+ @exceptions_to_xmpp_errors
16
+ async def on_chatstate_active(self, msg: StanzaBase) -> None:
17
+ assert isinstance(msg, Message)
18
+ if msg["body"]:
19
+ # if there is a body, it's handled in on_legacy_message()
20
+ return
21
+ session, entity, thread = await self._get_session_entity_thread(msg)
22
+ await session.on_active(entity, thread)
23
+
24
+ @exceptions_to_xmpp_errors
25
+ async def on_chatstate_inactive(self, msg: StanzaBase) -> None:
26
+ assert isinstance(msg, Message)
27
+ session, entity, thread = await self._get_session_entity_thread(msg)
28
+ await session.on_inactive(entity, thread)
29
+
30
+ @exceptions_to_xmpp_errors
31
+ async def on_chatstate_composing(self, msg: StanzaBase) -> None:
32
+ assert isinstance(msg, Message)
33
+ session, entity, thread = await self._get_session_entity_thread(msg)
34
+ await session.on_composing(entity, thread)
35
+
36
+ @exceptions_to_xmpp_errors
37
+ async def on_chatstate_paused(self, msg: StanzaBase) -> None:
38
+ assert isinstance(msg, Message)
39
+ session, entity, thread = await self._get_session_entity_thread(msg)
40
+ await session.on_paused(entity, thread)
@@ -0,0 +1,62 @@
1
+ from slixmpp import JID, Message
2
+ from slixmpp.xmlstream import StanzaBase
3
+
4
+ from ....group.room import LegacyMUC
5
+ from ....util.types import Recipient
6
+ from ..util import DispatcherMixin, _get_entity, exceptions_to_xmpp_errors
7
+
8
+
9
+ class MarkerMixin(DispatcherMixin):
10
+ def __init__(self, xmpp) -> None:
11
+ super().__init__(xmpp)
12
+ xmpp.add_event_handler("marker_displayed", self.on_marker_displayed)
13
+ xmpp.add_event_handler(
14
+ "message_displayed_synchronization_publish",
15
+ self.on_message_displayed_synchronization_publish,
16
+ )
17
+
18
+ @exceptions_to_xmpp_errors
19
+ async def on_marker_displayed(self, msg: StanzaBase) -> None:
20
+ assert isinstance(msg, Message)
21
+ session = await self._get_session(msg)
22
+
23
+ e: Recipient = await _get_entity(session, msg)
24
+ legacy_thread = await self._xmpp_to_legacy_thread(session, msg, e)
25
+ displayed_msg_id = msg["displayed"]["id"]
26
+ if not isinstance(e, LegacyMUC) and self.xmpp.MARK_ALL_MESSAGES:
27
+ to_mark = e.get_msg_xmpp_id_up_to(displayed_msg_id) # type: ignore
28
+ if to_mark is None:
29
+ session.log.debug("Can't mark all messages up to %s", displayed_msg_id)
30
+ to_mark = [displayed_msg_id]
31
+ else:
32
+ to_mark = [displayed_msg_id]
33
+ for xmpp_id in to_mark:
34
+ await session.on_displayed(
35
+ e, self._xmpp_msg_id_to_legacy(session, xmpp_id), legacy_thread
36
+ )
37
+ if isinstance(e, LegacyMUC):
38
+ await e.echo(msg, None)
39
+
40
+ @exceptions_to_xmpp_errors
41
+ async def on_message_displayed_synchronization_publish(
42
+ self, msg: StanzaBase
43
+ ) -> None:
44
+ assert isinstance(msg, Message)
45
+ chat_jid = JID(msg["pubsub_event"]["items"]["item"]["id"])
46
+ if chat_jid.server != self.xmpp.boundjid.bare:
47
+ return
48
+
49
+ session = await self._get_session(msg, timeout=None)
50
+
51
+ if chat_jid == self.xmpp.boundjid.bare:
52
+ return
53
+
54
+ chat = await session.get_contact_or_group_or_participant(chat_jid)
55
+ if not isinstance(chat, LegacyMUC):
56
+ session.log.debug("Ignoring non-groupchat MDS event")
57
+ return
58
+
59
+ stanza_id = msg["pubsub_event"]["items"]["item"]["displayed"]["stanza_id"]["id"]
60
+ await session.on_displayed(
61
+ chat, self._xmpp_msg_id_to_legacy(session, stanza_id)
62
+ )