slidge 0.2.0a9__py3-none-any.whl → 0.2.0b0__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 (56) hide show
  1. slidge/__main__.py +2 -3
  2. slidge/__version__.py +1 -1
  3. slidge/command/adhoc.py +1 -1
  4. slidge/command/base.py +4 -4
  5. slidge/command/user.py +5 -1
  6. slidge/contact/roster.py +9 -0
  7. slidge/core/config.py +0 -3
  8. slidge/core/dispatcher/__init__.py +3 -0
  9. slidge/core/{gateway → dispatcher}/caps.py +6 -4
  10. slidge/core/{gateway → dispatcher}/disco.py +11 -17
  11. slidge/core/dispatcher/message/__init__.py +10 -0
  12. slidge/core/dispatcher/message/chat_state.py +40 -0
  13. slidge/core/dispatcher/message/marker.py +62 -0
  14. slidge/core/dispatcher/message/message.py +397 -0
  15. slidge/core/dispatcher/muc/__init__.py +12 -0
  16. slidge/core/dispatcher/muc/admin.py +98 -0
  17. slidge/core/{gateway → dispatcher/muc}/mam.py +26 -15
  18. slidge/core/dispatcher/muc/misc.py +121 -0
  19. slidge/core/dispatcher/muc/owner.py +96 -0
  20. slidge/core/{gateway → dispatcher/muc}/ping.py +10 -15
  21. slidge/core/dispatcher/presence.py +177 -0
  22. slidge/core/{gateway → dispatcher}/registration.py +23 -2
  23. slidge/core/{gateway → dispatcher}/search.py +9 -14
  24. slidge/core/dispatcher/session_dispatcher.py +84 -0
  25. slidge/core/dispatcher/util.py +174 -0
  26. slidge/core/{gateway/vcard_temp.py → dispatcher/vcard.py} +26 -12
  27. slidge/core/{gateway/base.py → gateway.py} +42 -137
  28. slidge/core/mixins/attachment.py +24 -8
  29. slidge/core/mixins/base.py +2 -2
  30. slidge/core/mixins/lock.py +10 -8
  31. slidge/core/mixins/message.py +9 -203
  32. slidge/core/mixins/message_text.py +211 -0
  33. slidge/core/pubsub.py +2 -1
  34. slidge/core/session.py +28 -2
  35. slidge/db/alembic/versions/15b0bd83407a_remove_bogus_unique_constraints_on_room_.py +83 -0
  36. slidge/db/alembic/versions/45c24cc73c91_add_bob.py +42 -0
  37. slidge/db/alembic/versions/5bd48bfdffa2_lift_room_legacy_id_constraint.py +12 -1
  38. slidge/db/models.py +16 -1
  39. slidge/db/store.py +144 -11
  40. slidge/group/bookmarks.py +23 -1
  41. slidge/group/participant.py +5 -5
  42. slidge/group/room.py +10 -1
  43. slidge/{core/gateway → slixfix}/delivery_receipt.py +1 -1
  44. slidge/util/test.py +9 -5
  45. slidge/util/types.py +6 -0
  46. slidge/util/util.py +5 -2
  47. {slidge-0.2.0a9.dist-info → slidge-0.2.0b0.dist-info}/METADATA +2 -1
  48. {slidge-0.2.0a9.dist-info → slidge-0.2.0b0.dist-info}/RECORD +51 -40
  49. slidge-0.2.0b0.dist-info/entry_points.txt +3 -0
  50. slidge/core/gateway/__init__.py +0 -3
  51. slidge/core/gateway/muc_admin.py +0 -35
  52. slidge/core/gateway/presence.py +0 -95
  53. slidge/core/gateway/session_dispatcher.py +0 -895
  54. slidge-0.2.0a9.dist-info/entry_points.txt +0 -3
  55. {slidge-0.2.0a9.dist-info → slidge-0.2.0b0.dist-info}/LICENSE +0 -0
  56. {slidge-0.2.0a9.dist-info → slidge-0.2.0b0.dist-info}/WHEEL +0 -0
slidge/__main__.py CHANGED
@@ -1,4 +1,3 @@
1
- if __name__ == "__main__":
2
- from slidge.main import main
1
+ from slidge.main import main
3
2
 
4
- main()
3
+ main()
slidge/__version__.py CHANGED
@@ -2,4 +2,4 @@ from slidge.util.util import get_version # noqa: F401
2
2
 
3
3
  # this is modified before publish, but if someone cloned from the repo,
4
4
  # it can help
5
- __version__ = "0.2.0alpha9"
5
+ __version__ = "0.2.0beta0"
slidge/command/adhoc.py CHANGED
@@ -13,7 +13,7 @@ from . import Command, CommandResponseType, Confirmation, Form, TableResult
13
13
  from .base import FormField
14
14
 
15
15
  if TYPE_CHECKING:
16
- from ..core.gateway.base import BaseGateway
16
+ from ..core.gateway import BaseGateway
17
17
  from ..core.session import BaseSession
18
18
 
19
19
 
slidge/command/base.py CHANGED
@@ -6,9 +6,9 @@ from typing import (
6
6
  Any,
7
7
  Awaitable,
8
8
  Callable,
9
- Collection,
10
9
  Iterable,
11
10
  Optional,
11
+ Sequence,
12
12
  Type,
13
13
  TypedDict,
14
14
  Union,
@@ -54,11 +54,11 @@ class TableResult:
54
54
  Structured data as the result of a command
55
55
  """
56
56
 
57
- fields: Collection["FormField"]
57
+ fields: Sequence["FormField"]
58
58
  """
59
59
  The 'columns names' of the table.
60
60
  """
61
- items: Collection[dict[str, Union[str, JID]]]
61
+ items: Sequence[dict[str, Union[str, JID]]]
62
62
  """
63
63
  The rows of the table. Each row is a dict where keys are the fields ``var``
64
64
  attribute.
@@ -149,7 +149,7 @@ class Form:
149
149
 
150
150
  title: str
151
151
  instructions: str
152
- fields: Collection["FormField"]
152
+ fields: Sequence["FormField"]
153
153
  handler: FormHandlerType
154
154
  handler_args: Iterable[Any] = field(default_factory=list)
155
155
  handler_kwargs: dict[str, Any] = field(default_factory=dict)
slidge/command/user.py CHANGED
@@ -305,7 +305,10 @@ class LeaveGroup(Command):
305
305
  FormField(
306
306
  "group",
307
307
  "Group name",
308
- options=[{"label": g.name, "value": g.name} for g in groups],
308
+ type="list-single",
309
+ options=[
310
+ {"label": g.name, "value": str(i)} for i, g in enumerate(groups)
311
+ ],
309
312
  )
310
313
  ],
311
314
  handler=self.confirm, # type:ignore
@@ -329,3 +332,4 @@ class LeaveGroup(Command):
329
332
  @staticmethod
330
333
  async def finish(session: AnyBaseSession, _ifrom, group: LegacyMUC):
331
334
  await session.on_leave_group(group.legacy_id)
335
+ await session.bookmarks.remove(group, reason="You left this group via slidge.")
slidge/contact/roster.py CHANGED
@@ -92,6 +92,13 @@ class LegacyRoster(
92
92
  stored = self.__store.get_by_jid(self.session.user_pk, contact_jid)
93
93
  return await self.__update_contact(stored, legacy_id, username)
94
94
 
95
+ def by_jid_only_if_exists(self, contact_jid: JID) -> LegacyContactType | None:
96
+ with self.__store.session():
97
+ stored = self.__store.get_by_jid(self.session.user_pk, contact_jid)
98
+ if stored is not None and stored.updated:
99
+ return self._contact_cls.from_store(self.session, stored)
100
+ return None
101
+
95
102
  async def by_legacy_id(
96
103
  self, legacy_id: LegacyUserIdType, *args, **kwargs
97
104
  ) -> LegacyContactType:
@@ -147,6 +154,8 @@ class LegacyRoster(
147
154
  try:
148
155
  with contact.updating_info():
149
156
  await contact.avatar_wrap_update_info()
157
+ except XMPPError:
158
+ raise
150
159
  except Exception as e:
151
160
  raise XMPPError("internal-server-error", str(e))
152
161
  contact._caps_ver = await contact.get_caps_ver(contact.jid)
slidge/core/config.py CHANGED
@@ -154,9 +154,6 @@ LAST_SEEN_FALLBACK__DOC = (
154
154
  QR_TIMEOUT = 60
155
155
  QR_TIMEOUT__DOC = "Timeout for QR code flashing confirmation."
156
156
 
157
- DOWNLOAD_CHUNK_SIZE = 1024
158
- DOWNLOAD_CHUNK_SIZE__DOC = "Chunk size when slidge needs to download files using HTTP."
159
-
160
157
  LAST_MESSAGE_CORRECTION_RETRACTION_WORKAROUND = False
161
158
  LAST_MESSAGE_CORRECTION_RETRACTION_WORKAROUND__DOC = (
162
159
  "If the legacy service does not support last message correction but supports"
@@ -0,0 +1,3 @@
1
+ from .session_dispatcher import SessionDispatcher
2
+
3
+ __all__ = ("SessionDispatcher",)
@@ -5,17 +5,19 @@ from slixmpp import Presence
5
5
  from slixmpp.exceptions import XMPPError
6
6
  from slixmpp.xmlstream import StanzaBase
7
7
 
8
+ from .util import DispatcherMixin
9
+
8
10
  if TYPE_CHECKING:
9
- from .base import BaseGateway
11
+ from slidge.core.gateway import BaseGateway
10
12
 
11
13
 
12
- class Caps:
14
+ class CapsMixin(DispatcherMixin):
13
15
  def __init__(self, xmpp: "BaseGateway"):
14
- self.xmpp = xmpp
16
+ super().__init__(xmpp)
15
17
  xmpp.del_filter("out", xmpp.plugin["xep_0115"]._filter_add_caps)
16
18
  xmpp.add_filter("out", self._filter_add_caps) # type:ignore
17
19
 
18
- async def _filter_add_caps(self, stanza: StanzaBase):
20
+ async def _filter_add_caps(self, stanza: StanzaBase) -> StanzaBase:
19
21
  # we rolled our own "add caps on presences" filter because
20
22
  # there is too much magic happening in slixmpp
21
23
  # anyway, we probably want to roll our own "dynamic disco"/caps
@@ -5,13 +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 import DispatcherMixin
9
+
8
10
  if TYPE_CHECKING:
9
- from .base import BaseGateway
11
+ from slidge.core.gateway import BaseGateway
10
12
 
11
13
 
12
- class Disco:
14
+ class DiscoMixin(DispatcherMixin):
13
15
  def __init__(self, xmpp: "BaseGateway"):
14
- self.xmpp = xmpp
16
+ super().__init__(xmpp)
15
17
 
16
18
  xmpp.plugin["xep_0030"].set_node_handler(
17
19
  "get_info",
@@ -36,15 +38,11 @@ class Disco:
36
38
  if ifrom is None:
37
39
  raise XMPPError("subscription-required")
38
40
 
39
- user = self.xmpp.store.users.get(ifrom)
40
- if user is None:
41
- raise XMPPError("registration-required")
42
- session = self.xmpp.get_session_from_user(user)
43
- await session.wait_for_ready()
41
+ assert jid is not None
42
+ session = await self._get_session_from_jid(jid=ifrom)
44
43
 
45
44
  log.debug("Looking for entity: %s", jid)
46
45
 
47
- assert jid is not None
48
46
  entity = await session.get_contact_or_group_or_participant(jid)
49
47
 
50
48
  if entity is None:
@@ -61,16 +59,12 @@ class Disco:
61
59
  if jid != self.xmpp.boundjid.bare:
62
60
  return DiscoItems()
63
61
 
64
- user = self.xmpp.store.users.get(ifrom)
65
- if user is None:
66
- raise XMPPError("registration-required")
67
-
68
- session = self.xmpp.get_session_from_user(user)
69
- await session.wait_for_ready()
62
+ assert ifrom is not None
63
+ session = await self._get_session_from_jid(ifrom)
70
64
 
71
65
  d = DiscoItems()
72
- for muc in sorted(session.bookmarks, key=lambda m: m.name):
73
- 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)
74
68
 
75
69
  return d
76
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
+ )